Example #1
0
/*
 * Refresh the server status by pinging or testing the interface in the given interval.
 * Note that you may get inaccuracies in the dimension of the ping timeout or the runtime
 * of your uptest command if you have uptest=ping or uptest=exec for at least one server.
 * This happens when all the uptests for the first n servers take more time than the inteval
 * of n+1 (or 0 when n+1>servnum). I do not think that these delays are critical, so I did
 * not to anything about that (because that may also be costly).
 */
void *servstat_thread(void *p)
{
	struct sigaction action;
	int keep_testing;

	/* (void)p; */  /* To inhibit "unused variable" warning */

	THREAD_SIGINIT;

	pthread_mutex_lock(&servers_lock);
	/* servstat_thrid=pthread_self(); */

	signal_interrupt=0;
	action.sa_handler = sigint_handler;
	sigemptyset(&action.sa_mask);
	action.sa_flags = 0;
	if(sigaction(statusintsig, &action, NULL) == 0) {
		sigset_t smask;
		sigemptyset(&smask);
		sigaddset(&smask, statusintsig); 
		pthread_sigmask(SIG_UNBLOCK,&smask,NULL);
	}
	else {
		log_warn("Cannot install signal handler for server status thread: %s\n",strerror(errno));
	}

	for(;;) {
		do {
			int i,n;
			keep_testing=0;
			retest_flag=0;
			schm[0] = '\0';
			n=DA_NEL(servers);
			for (i=0;i<n;++i) {
				servparm_t *sp=&DA_INDEX(servers,i);
				int j,m;
				if(sp->rootserver==2) {
					/* First get addresses of root servers. */
					addr2_array adrs;
					int l, one_up=0;

					if(!scheme_ok(sp)) {
						time_t now=time(NULL);
						m=DA_NEL(sp->atup_a);
						for(j=0;j<m;++j)
							DA_INDEX(sp->atup_a,j).i_ts=now;
					} else if(sp->uptest==C_PING || sp->uptest==C_QUERY) {
						/* Skip ping or query tests until after discovery. */
						if(sp->interval>0)
							one_up= DA_NEL(sp->atup_a);
						else {
							time_t now=time(NULL);
							m=DA_NEL(sp->atup_a);
							for(j=0;j<m;++j) {
								atup_t *at=&DA_INDEX(sp->atup_a,j);
								if(at->is_up || at->i_ts==0)
									one_up=1;
								at->i_ts=now;
							}
						}
					}
					else {
						retest(i,-1);

						m=DA_NEL(sp->atup_a);
						for(j=0;j<m;++j) {
							if(DA_INDEX(sp->atup_a,j).is_up) {
								one_up=1;
								break;
							}
						}
					}

					if(!one_up) {
						if (needs_intermittent_testing(sp)) keep_testing=1;
						continue;
					}

					DEBUG_MSG("Attempting to discover root servers for server section #%d.\n",i);
					adrs=resolv_rootserver_addrs(sp->atup_a,sp->port,sp->timeout);
					l= DA_NEL(adrs);
					if(l>0) {
						struct timeval now;
						struct timespec timeout;
						atup_array ata;
						DEBUG_MSG("Filling server section #%d with %d root server addresses.\n",i,l);
						gettimeofday(&now,NULL);
						timeout.tv_sec = now.tv_sec + 60;     /* time out after 60 seconds */
						timeout.tv_nsec = now.tv_usec * 1000;
						while (server_data_users>0) {
							if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) {
								DEBUG_MSG("Timed out while waiting for exclusive access to server data"
									  " to set root server addresses of server section #%d\n",i);
								da_free(adrs);
								keep_testing=1;
								continue;
							}
						}
						ata = DA_CREATE(atup_array, l);
						if(!ata) {
							log_warn("Out of memory in servstat_thread() while discovering root servers.");
							da_free(adrs);
							keep_testing=1;
							continue;
						}
						for(j=0; j<l; ++j) {
							atup_t *at = &DA_INDEX(ata,j);
							at->a = DA_INDEX(adrs,j);
							at->is_up=sp->preset;
							at->i_ts= sp->interval<0 ? time(NULL): 0;
						}
						da_free(sp->atup_a);
						sp->atup_a=ata;
						da_free(adrs);
						/* Successfully set IP addresses for this server section. */
						sp->rootserver=1;
					}
					else {
						DEBUG_MSG("Failed to discover root servers in servstat_thread() (server section #%d).\n",i);
						if(adrs) da_free(adrs);
						if(DA_NEL(sp->atup_a)) keep_testing=1;
						continue;
					}
				}

				if (needs_testing(sp)) keep_testing=1;
				m=DA_NEL(sp->atup_a);
				for(j=0;j<m;++j)
					if(DA_INDEX(sp->atup_a,j).i_ts)
						goto individual_tests;
				/* Test collectively */
				if(!signal_interrupt) retest(i,-1);
				continue;

			individual_tests:
				for(j=0; !signal_interrupt && j<m; ++j) {
					time_t ts=DA_INDEX(sp->atup_a,j).i_ts, now;

					if (ts==0 /* Always test servers with timestamp 0 */ ||
					    (needs_intermittent_testing(sp) &&
					     ((now=time(NULL))-ts>sp->interval ||
					      ts>now /* kluge for clock skew */)))
					{ 
						retest(i,j);
					}
				}
			}
		} while(!signal_interrupt && retest_flag);

		signal_interrupt=0;

		/* Break the loop and exit the thread if it is no longer needed. */
		if(!keep_testing) break;

		{
			struct timeval now;
			struct timespec timeout;
			time_t minwait;
			int i,n,retval;

			gettimeofday(&now,NULL);
			minwait=3600; /* Check at least once every hour. */
			n=DA_NEL(servers);
			for (i=0;i<n;++i) {
				servparm_t *sp=&DA_INDEX(servers,i);
				int j,m=DA_NEL(sp->atup_a);
				for(j=0;j<m;++j) {
					time_t ts= DA_INDEX(sp->atup_a,j).i_ts;
					if(ts==0) {
						/* Test servers with timestamp 0 without delay */
						if(minwait > 0) minwait=0;
					}
					else if(needs_intermittent_testing(sp)) {
						time_t wait= ts + sp->interval - now.tv_sec;
						if(wait < minwait) minwait=wait;
					}
				}
			}
			timeout.tv_sec = now.tv_sec;
			if(minwait>0)
				timeout.tv_sec += minwait;
			timeout.tv_nsec = now.tv_usec * 1000 + 500000000;  /* wait at least half a second. */
			if(timeout.tv_nsec>=1000000000) {
				timeout.tv_nsec -= 1000000000;
				++timeout.tv_sec;
			}
			/* While we wait for a server_test_cond condition or a timeout
			   the servers_lock mutex is unlocked, so other threads can access
			   server data
			*/
			retval=pthread_cond_timedwait(&server_test_cond, &servers_lock, &timeout);
			DEBUG_MSG("Server status thread woke up (%s signal).\n",
				  retval==0?"test condition":retval==ETIMEDOUT?"timer":retval==EINTR?"interrupt":"error");
		}
	}

	/* server status thread no longer needed. */
	servstat_thrid=main_thrid;
	pthread_mutex_unlock(&servers_lock);
	DEBUG_MSG("Server status thread exiting.\n");
	return NULL;
}
Example #2
0
/*
 * Execute an individual uptest. Call with locks applied 
 */
static int uptest (servparm_t *serv, int j)
{
	int ret=0, count_running_ping=0;
	pdnsd_a *s_addr= PDNSD_A2_TO_A(&DA_INDEX(serv->atup_a,j).a);

	DEBUG_PDNSDA_MSG("performing uptest (type=%s) for %s\n",const_name(serv->uptest),PDNSDA2STR(s_addr));

	/* Unlock the mutex because some of the tests may take a while. */
	++server_data_users;
	if((serv->uptest==C_PING || serv->uptest==C_QUERY) && pthread_equal(pthread_self(),servstat_thrid)) {
		/* Inform other threads that a ping is in progress. */
		count_running_ping=1;
		++server_status_ping;
	}
	pthread_mutex_unlock(&servers_lock);

	switch (serv->uptest) {
	case C_NONE:
		/* Don't change */
		ret=DA_INDEX(serv->atup_a,j).is_up;
		break;
	case C_PING:
		ret=ping(is_inaddr_any(&serv->ping_a) ? s_addr : &serv->ping_a, serv->ping_timeout,PINGREPEAT)!=-1;
		break;
	case C_IF:
 	case C_DEV:
	case C_DIALD:
 		ret=if_up(serv->interface);
#if (TARGET==TARGET_LINUX)
 		if (ret!=0) {
			if(serv->uptest==C_DEV)
				ret=dev_up(serv->interface,serv->device);
			else if (serv->uptest==C_DIALD)
				ret=dev_up("diald",serv->device);
 		}
#endif
		break;
	case C_EXEC: {
	  	pid_t pid;

		if ((pid=fork())==-1) {
			DEBUG_MSG("Could not fork to perform exec uptest: %s\n",strerror(errno));
			break;
		} else if (pid==0) { /* child */
			/*
			 * If we ran as setuid or setgid, do not inherit this to the
			 * command. This is just a last guard. Running pdnsd as setuid()
			 * or setgid() is a no-no.
			 */
			if (setgid(getgid()) == -1 || setuid(getuid()) == -1) {
				log_error("Could not reset uid or gid: %s",strerror(errno));
				_exit(1);
			}
			/* Try to setuid() to a different user as specified. Good when you
			   don't want the test command to run as root */
			if (!run_as(serv->uptest_usr)) {
				_exit(1);
			}
			{
			    struct rlimit rl; int i;
			    /*
			     * Mark all open fd's FD_CLOEXEC for paranoia reasons.
			     */
			    if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
				    log_error("getrlimit() failed: %s",strerror(errno));
				    _exit(1);
			    }
			    for (i = 0; i < rl.rlim_max; i++) {
				    if (fcntl(i, F_SETFD, FD_CLOEXEC) == -1 && errno != EBADF) {
					    log_error("fcntl(F_SETFD) failed: %s",strerror(errno));
					    _exit(1);
				    }
			    }
			}
			execl("/bin/sh", "uptest_sh","-c",serv->uptest_cmd,(char *)NULL);
			_exit(1); /* failed execl */
		} else { /* parent */
			int status;
			pid_t wpid = waitpid(pid,&status,0);
			if (wpid==pid) {
				if(WIFEXITED(status)) {
					int exitstatus=WEXITSTATUS(status);
					DEBUG_MSG("uptest command \"%s\" exited with status %d\n",
						  serv->uptest_cmd, exitstatus);
					ret=(exitstatus==0);
				}
#if DEBUG>0
				else if(WIFSIGNALED(status)) {
					DEBUG_MSG("uptest command \"%s\" was terminated by signal %d\n",
						  serv->uptest_cmd, WTERMSIG(status));
				}
				else {
					DEBUG_MSG("status of uptest command \"%s\" is of unkown type (0x%x)\n",
						  serv->uptest_cmd, status);
				}
#endif
			}
#if DEBUG>0
			else if (wpid==-1) {
				DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: "
					  "waitpid for pid %d failed: %s\n",
					  serv->uptest_cmd, pid, strerror(errno));
			}
			else {
				DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: "
					  "waitpid returned %d, expected pid %d\n",
					  serv->uptest_cmd, wpid, pid);
			}
#endif
		}
	}
		break;
	case C_QUERY:
		ret=query_uptest(s_addr, serv->port,
				 serv->timeout>=global.timeout?serv->timeout:global.timeout,
				 PINGREPEAT);
	} /* end of switch */

	pthread_mutex_lock(&servers_lock);
	if(count_running_ping)
		--server_status_ping;
	PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it");
	if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond);

	DEBUG_PDNSDA_MSG("result of uptest for %s: %s\n",
			 PDNSDA2STR(s_addr),
			 ret?"OK":"failed");
	return ret;
}
Example #3
0
/*
 * Re-Read the configuration file.
 * Return 1 on success, 0 on failure.
 * In case of failure, the old configuration will be unchanged (although the cache may not) and
 * **errstr will refer to a newly allocated string containing an error message.
 */
int reload_config_file(const char *nm, char **errstr)
{
	globparm_t global_new;
	servparm_array servers_new;

	global_new=global;
	global_new.cache_dir=NULL;
	global_new.pidfile=NULL;
	global_new.scheme_file=NULL;
	global_new.deleg_only_zones=NULL;
	global_new.onquery=0;
	servers_new=NULL;
	if(read_config_file(nm,&global_new,&servers_new,0,errstr)) {
		if(global_new.cache_dir && strcmp(global_new.cache_dir,global.cache_dir)) {
			*errstr=strdup("Cannot reload config file: the specified cache_dir directory has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.pidfile && (!global.pidfile || strcmp(global_new.pidfile,global.pidfile))) {
			*errstr=strdup("Cannot reload config file: the specified pid_file has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.scheme_file && strcmp(global_new.scheme_file,global.scheme_file)) {
			*errstr=strdup("Cannot reload config file: the specified scheme_file has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.port!=global.port) {
			*errstr=strdup("Cannot reload config file: the specified server_port has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(!ADDR_EQUIV(&global_new.a,&global.a)) {
			*errstr=strdup("Cannot reload config file: the specified interface address (server_ip) has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
#ifdef ENABLE_IPV6
		if(!IN6_ARE_ADDR_EQUAL(&global_new.ipv4_6_prefix,&global.ipv4_6_prefix)) {
			*errstr=strdup("Cannot reload config file: the specified ipv4_6_prefix has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
#endif
		if(strcmp(global_new.run_as,global.run_as)) {
			*errstr=strdup("Cannot reload config file: the specified run_as id has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.daemon!=global.daemon) {
			*errstr=strdup("Cannot reload config file: the daemon option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.debug!=global.debug) {
			*errstr=strdup("Cannot reload config file: the debug option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.stat_pipe!=global.stat_pipe) {
			*errstr=strdup("Cannot reload config file: the status_ctl option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.notcp!=global.notcp) {
			*errstr=strdup("Cannot reload config file: the tcp_server option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.strict_suid!=global.strict_suid) {
			*errstr=strdup("Cannot reload config file: the strict_setuid option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.ctl_perms!=global.ctl_perms) {
			*errstr=strdup("Cannot reload config file: the specified ctl_perms has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(ping_isocket==-1
#ifdef ENABLE_IPV6
		   && ping6_isocket==-1
#endif
		  ) {
			int i,n=DA_NEL(servers_new);
			for (i=0;i<n;++i) {
				if (DA_INDEX(servers_new,i).uptest==C_PING) {
					if(asprintf(errstr,"Cannot reload config file: the ping socket is not initialized"
						    " and the new config contains uptest=ping in server section %i.\n"
						    "Try restarting pdnsd instead.",i)<0)
						*errstr=NULL;
					goto cleanup_return;
				}
			}
		}

		/* we need exclusive access to the server data to make the changes */
		/* Wait at most 60 seconds to obtain a lock. */
		if(!exclusive_lock_server_data(60)) {
			*errstr=strdup("Cannot reload config file: Timed out while waiting for access to config data.");
			goto cleanup_return;
		}
		free(global_new.cache_dir); global_new.cache_dir=global.cache_dir;
		free(global_new.pidfile); global_new.pidfile=global.pidfile;
		free(global_new.scheme_file); global_new.scheme_file=global.scheme_file;
		free_zones(global.deleg_only_zones);
		global=global_new;

		free_server_data(servers);
		servers=servers_new;
		/* schedule a retest to check which servers are up,
		   and free the lock. */
		exclusive_unlock_server_data(1);

		return 1;
	}

 cleanup_return:
	free(global_new.cache_dir);
	free(global_new.pidfile);
	free(global_new.scheme_file);
	free_zones(global_new.deleg_only_zones);
	free_server_data(servers_new);
	return 0;
}