view usr/src/cmd/agents/snmp/agent/snmpd.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2002, 2003 by Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)snmpd.c	1.21	05/06/12 SMI"

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <syslog.h>
#include "impl.h"
#include "error.h"
#include "trace.h"
#include "signals.h"
#include "snmp.h"
#include "pdu.h"
#include "agent_msg.h"
#include "agent.h"
#include "config.h"

/***** DEFINES *****/

#define DEFAULT_POLL_INTERVAL		30

/***** IMPORTED VARIABLES *****/

/* user defined data */

extern char default_config_file[];
extern char default_sec_config_file[];
extern char default_error_file[];

/***** IMPORTED FUNCTIONS *****/

/* user defined functions */
extern void agent_init();
extern void agent_end();
extern void agent_loop();
extern void agent_select_info(fd_set *fdset, int *numfds);
extern void agent_select_callback(fd_set *fdset);

/***** STATIC VARIABLES *****/

static int sighup = False;
char *config_file = NULL;
char *sec_config_file = NULL;
int agent_port_number = -1;

int dont_read_config_file = FALSE;

static int poll_interval = DEFAULT_POLL_INTERVAL;
int max_agent_reg_retry = 10;

/***** LOCAL FUNCTIONS *****/

static void signals_sighup(int siq);
static void signals_exit(int siq);

static int snmpd_init(int port);
static void print_usage(char *command_name);
static void snmpd_loop(int sd);
static void sap_main(int, char **);


/********************************************************************/

static void
application_end()
{
	agent_end();
}


/********************************************************************/

static void
signals_sighup(int sig)
{
	if(trace_level > 0)
		trace("received signal SIGHUP(%d)\n\n", sig);

	error(MSG_SIGHUP, sig);

	sighup = True;
}


/********************************************************************/

static void
signals_exit(int sig)
{

	if (trace_level > 0)
		trace("received signal %d", sig);

	application_end();

	exit(1);
}


/********************************************************************/

static int
snmpd_init(int port)
{
	int sd;
	struct sockaddr_in me;

	/* init the config_file pointer and then parse the configuration file */

	if (port > 0)
		agent_port_number = port;

	if (dont_read_config_file == FALSE) {
		if (config_file == NULL)
			config_file = default_config_file;
		config_init(config_file);
	}

	if (sec_config_file == NULL)
		sec_config_file = default_sec_config_file;

	(void)sec_config_init(sec_config_file);

	/* successfully register the subagent, then set the operation status of
	 * subagent to run.
	 */
	sd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sd < 0)
		error_exit(ERR_MSG_SOCKET, errno_string());

	/* evaluate the port to be used, the port priority is :
	  command port > config. file port > def. port */
	if(port == 0 && agent_port_number != 0){
		port = agent_port_number;
	}

	me.sin_family = AF_INET;
	me.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	me.sin_port = htons(port);
	if(bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0)
		error_exit(ERR_MSG_BIND, port, errno_string());

	if(trace_level > 0)
		trace("Waiting for incoming SNMP requests on UDP port %d\n\n", port);

	return sd;
}

static void
snmpd_loop(int sd)
{
	int numfds;
	fd_set fdset;
	int count;
	long long timer;

	struct timeval expire;
	struct timeval timeout;
	struct timeval now;

	expire.tv_sec = 0;
	expire.tv_usec = 0;


	/* CONSTCOND */
	while (1) {
		if (sighup) {
			error(MSG_READING_CONFIG,
				config_file);

			config_init(config_file);

			error(MSG_READING_CONFIG, sec_config_file);

			(void) sec_config_init(sec_config_file);

			error(MSG_CONFIG_READED);

			sighup = False;
		}

		numfds = 0;
		FD_ZERO(&fdset);

		numfds = sd + 1;
		FD_SET(sd, &fdset);

		agent_select_info(&fdset, &numfds);

		(void) gettimeofday(&now, (struct timezone *)0);

		timer = (long long) (now.tv_sec - expire.tv_sec) * 1000000;
		timer += (long long) (now.tv_usec - expire.tv_usec);

		if (timer >= 0) {
			/* now > timeval + poll_interval */
			timeout.tv_sec = 0;
			timeout.tv_usec = 0;
		} else {
			timeout.tv_sec = -timer / 1000000;
			timeout.tv_usec = -timer % 1000000;
		}

		count = select(numfds, &fdset, 0, 0, &timeout);
		if (count > 0) {
			if (FD_ISSET(sd, &fdset)) {
				Address address;
				SNMP_pdu *pdu;

				if ((pdu = snmp_pdu_receive(sd, &address,
						error_label)) == NULL) {
					error(ERR_MSG_PDU_RECEIVED,
						address_string(&address),
						error_label);
					continue;
				}

				if (agent_process(&address, pdu) == -1) {
					error(ERR_MSG_PDU_PROCESS,
						address_string(&address));
					snmp_pdu_free(pdu);
					continue;
				}

				if (pdu->error_status ==
					SNMP_ERR_AUTHORIZATIONERROR) {
						snmp_pdu_free(pdu);
						continue;
				}

				if ((pdu->error_status != SNMP_ERR_NOERROR)
				&& (pdu->error_status != SNMP_ERR_NOSUCHNAME)) {
					error(ERR_MSG_SNMP_ERROR,
					error_status_string(pdu->error_status),
						pdu->error_index,
						address_string(&address));
				}

				if (snmp_pdu_send(sd, &address, pdu,
						error_label) == -1) {
					error(ERR_MSG_PDU_SEND,
						address_string(&address),
						error_label);
					snmp_pdu_free(pdu);
					continue;
				}

				snmp_pdu_free(pdu);
			}

			agent_select_callback(&fdset);
		} else {
			switch (count) {
				case 0:
					(void) gettimeofday(&expire,
						(struct timezone *) 0);
					expire.tv_sec = expire.tv_sec + poll_interval;
					agent_loop();
					break;

				case -1:
					if (errno == EINTR) {
						continue;
					} else if (errno == EBADF) {
					FD_CLR(sd, &fdset);
					fprintf(stderr, "select() failed %s\n",
						errno_string());
					continue;
					} else {
						error_exit(ERR_MSG_SELECT,
							errno_string());
					}
			}
		}
	}
}


static void print_usage(char *command_name)
{
	(void)fprintf(stderr, "Usage: %s [-h]\n\
\t[-k (don't read config file)]\n\
\t[-p port ]\n\
\t[-c config-file (default %s)]\n\
\t[-a sec-config-file (default %s)]\n\
\t[-i poll-interval (default %d seconds)]\n\
\t[-d trace-level (range 0..%d, default %d)]\n\n",
		command_name,
		default_config_file,
		default_sec_config_file,
		DEFAULT_POLL_INTERVAL,
		TRACE_LEVEL_MAX,
		trace_level);
	exit(1);
}


static void
sap_main(argc, argv)
	int argc;
	char *argv[];
{
	int arg;
	int port = 0;
	int sd;
	char *str;
	int level;
	char *error_file = NULL;



	error_init(argv[0], application_end);

	/* parse arguments */

	for(arg = 1; arg < argc; arg++)
	{
		if(argv[arg][0] == '-')
		{
			switch(argv[arg][1])
			{
                                case 'k':
                                        dont_read_config_file = TRUE;
                                        break;
				case 'h':
				case '?':
					print_usage(argv[0]);

					/* never reached */
					return;

				case 'p':
					arg++;
					if(arg >= argc)
					{
						(void)fprintf(stderr, "Must have another argument following the -p option\n");
						print_usage(argv[0]);
					}

					/* LINTED */
					port = (int32_t)strtol(argv[arg], &str, 10);
					if(argv[arg] == str)
					{
						(void)fprintf(stderr, "Not a valid integer following the -p option: %s\n", argv[arg]);
						print_usage(argv[0]);
					}

					break;

				case 'c':
					arg++;
					if(arg >= argc)
					{
						(void)fprintf(stderr, "Must have a configuration file name following the -c option\n");
						print_usage(argv[0]);
					}

					config_file = (char *) strdup(argv[arg]);
					if(config_file == NULL)
					{
						(void)fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
						exit(1);
					}

					break;

				case 'a':
					arg++;
					if(arg >= argc)
					{
						(void)fprintf(stderr, "Must have a security configuration file name following the -a option\n");
						print_usage(argv[0]);
					}

					sec_config_file = (char *) strdup(argv[arg]);
					if(sec_config_file == NULL)
					{
						(void)fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
						exit(1);
					}

					break;


				case 'i':
					arg++;
					if(arg >= argc)
					{
						(void)fprintf(stderr, "Must have another argument following the -i option\n");
						print_usage(argv[0]);
					}

					/* LINTED */
					poll_interval = (int32_t)strtol(argv[arg], &str, 10);
					if(argv[arg] == str)
					{
						(void)fprintf(stderr, "Not a valid integer following the -i option: %s\n", argv[arg]);
						print_usage(argv[0]);
					}
					if(poll_interval <= 0)
					{
						(void)fprintf(stderr, "The poll-interval must be greater than 0: %d\n", poll_interval);
						print_usage(argv[0]);
					}

					break;

				case 'd':
					arg++;
					if(arg >= argc)
					{
						(void)fprintf(stderr, "Must have another argument following the -d option\n");
						print_usage(argv[0]);
					}

					/* LINTED */
					level = (int32_t)strtol(argv[arg], &str, 10);
					if(argv[arg] == str)
					{
						(void)fprintf(stderr, "Not a valid integer following the -d option: %s\n", argv[arg]);
						print_usage(argv[0]);
					}
					if(trace_set(level, error_label))
					{
						print_usage(argv[0]);
					}

					break;

				default:
					(void)fprintf(stderr, "Invalid option: -%c\n", argv[arg][1]);
					print_usage(argv[0]);
			}
			continue;
		}
	}


	if(error_file == NULL)
	{
		error_file = default_error_file;
	}
	error_open(error_file);

	if(trace_level == 0)
	{
		/* run the daemon in backgound */

		pid_t pid; 

		pid = fork();
		switch(pid)
		{
			case -1:
				error_exit(ERR_MSG_FORK, errno_string());

				/* never reached */
				return;

			case 0: /* child process */
				break;

			default: /* parent process */
				exit(0);
		}
	}

	if(fclose(stdin) == EOF) 
	{
		error(ERR_MSG_FCLOSE, "stdin", errno_string());
	}

	sd = snmpd_init(port);

	if(signals_init(signals_sighup, signals_exit, error_label))
	{
		error_exit("signals_init() failed: %s", error_label);
	}

	if(trace_level == 0)
	{
		if(fclose(stdout) == EOF)
		{
			error(ERR_MSG_FCLOSE, "stdout", errno_string());
		}
	}

	if(trace_level == 0)
	{
		/* backgound */

		if(chdir("/") == -1)
		{
			error(ERR_MSG_CHDIR, "/", errno_string());
		}

		/* set process group ID */
		(void)setpgrp();

		error_close_stderr();
	}

	/* have to be called after error_open() and error_close_stderr() */
	agent_init();

	snmpd_loop(sd);

	/* never reached */
}

void 
SSAMain(int argc, char** argv)
{
	sap_main(argc,argv);
}