| /* |
| * snmpd.c |
| */ |
| /** @defgroup agent The Net-SNMP agent |
| * The snmp agent responds to SNMP queries from management stations |
| */ |
| /* Portions of this file are subject to the following copyrights. See |
| * the Net-SNMP's COPYING file for more details and other copyrights |
| * that may apply: |
| */ |
| /* |
| * Copyright 1988, 1989 by Carnegie Mellon University |
| * |
| * All Rights Reserved |
| * |
| * Permission to use, copy, modify, and distribute this software and its |
| * documentation for any purpose and without fee is hereby granted, |
| * provided that the above copyright notice appear in all copies and that |
| * both that copyright notice and this permission notice appear in |
| * supporting documentation, and that the name of CMU not be |
| * used in advertising or publicity pertaining to distribution of the |
| * software without specific, written prior permission. |
| * |
| * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| * SOFTWARE. |
| * ***************************************************************** |
| */ |
| /* |
| * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms specified in the COPYING file |
| * distributed with the Net-SNMP package. |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| |
| #if HAVE_IO_H |
| #include <io.h> |
| #endif |
| #include <stdio.h> |
| #include <errno.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_SYS_SELECT_H |
| #include <sys/select.h> |
| #endif |
| #if HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #elif HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if HAVE_NET_IF_H |
| #include <net/if.h> |
| #endif |
| #if HAVE_INET_MIB2_H |
| #include <inet/mib2.h> |
| #endif |
| #if HAVE_SYS_IOCTL_H |
| #include <sys/ioctl.h> |
| #endif |
| #if HAVE_SYS_FILE_H |
| #include <sys/file.h> |
| #endif |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #if HAVE_SYS_WAIT_H |
| #include <sys/wait.h> |
| #endif |
| #include <signal.h> |
| #ifdef HAVE_SYS_PARAM_H |
| #include <sys/param.h> |
| #endif |
| #if HAVE_PROCESS_H /* Win32-getpid */ |
| #include <process.h> |
| #endif |
| #if HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #if HAVE_PWD_H |
| #include <pwd.h> |
| #endif |
| #if HAVE_GRP_H |
| #include <grp.h> |
| #endif |
| |
| #ifndef PATH_MAX |
| # ifdef _POSIX_PATH_MAX |
| # define PATH_MAX _POSIX_PATH_MAX |
| # else |
| # define PATH_MAX 255 |
| # endif |
| #endif |
| |
| #ifndef FD_SET |
| typedef long fd_mask; |
| #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ |
| #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) |
| #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) |
| #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) |
| #define FD_ZERO(p) memset((p), 0, sizeof(*(p))) |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include <net-snmp/library/fd_event_manager.h> |
| |
| #include "m2m.h" |
| #include <net-snmp/agent/mib_module_config.h> |
| |
| #include "snmpd.h" |
| #include "mibgroup/struct.h" |
| #include <net-snmp/agent/mib_modules.h> |
| |
| #include "mibgroup/util_funcs.h" |
| |
| #include <net-snmp/agent/agent_trap.h> |
| |
| #include <net-snmp/agent/table.h> |
| #include <net-snmp/agent/table_iterator.h> |
| #include "mib_module_includes.h" |
| |
| /* |
| * Include winservice.h to support Windows Service |
| */ |
| #ifdef WIN32 |
| #include <windows.h> |
| #include <tchar.h> |
| #include <net-snmp/library/winservice.h> |
| |
| #define WIN32SERVICE |
| |
| #endif |
| |
| /* |
| * Globals. |
| */ |
| #ifdef NETSNMP_USE_LIBWRAP |
| #include <tcpd.h> |
| #endif /* NETSNMP_USE_LIBWRAP */ |
| |
| #define TIMETICK 500000L |
| |
| int snmp_dump_packet; |
| int reconfig = 0; |
| int Facility = LOG_DAEMON; |
| |
| #ifdef WIN32SERVICE |
| /* |
| * SNMP Agent Status |
| */ |
| #define AGENT_RUNNING 1 |
| #define AGENT_STOPPED 0 |
| int agent_status = AGENT_STOPPED; |
| /* app_name_long used for Event Log (syslog), SCM, registry etc */ |
| LPCTSTR app_name_long = _T("Net-SNMP Agent"); /* Application Name */ |
| #endif |
| |
| const char *app_name = "snmpd"; |
| |
| extern int netsnmp_running; |
| extern char **argvrestartp; |
| extern char *argvrestart; |
| extern char *argvrestartname; |
| |
| #ifdef USING_SMUX_MODULE |
| #include <mibgroup/smux/smux.h> |
| #endif /* USING_SMUX_MODULE */ |
| |
| /* |
| * Prototypes. |
| */ |
| int snmp_read_packet(int); |
| int snmp_input(int, netsnmp_session *, int, netsnmp_pdu *, |
| void *); |
| static void usage(char *); |
| static void SnmpTrapNodeDown(void); |
| static int receive(void); |
| #ifdef WIN32SERVICE |
| void StopSnmpAgent(void); |
| int SnmpDaemonMain(int argc, TCHAR * argv[]); |
| int __cdecl _tmain(int argc, TCHAR * argv[]); |
| #else |
| int main(int, char **); |
| #endif |
| |
| /* |
| * These definitions handle 4.2 systems without additional syslog facilities. |
| */ |
| #ifndef LOG_CONS |
| #define LOG_CONS 0 /* Don't bother if not defined... */ |
| #endif |
| #ifndef LOG_PID |
| #define LOG_PID 0 /* Don't bother if not defined... */ |
| #endif |
| #ifndef LOG_LOCAL0 |
| #define LOG_LOCAL0 0 |
| #endif |
| #ifndef LOG_LOCAL1 |
| #define LOG_LOCAL1 0 |
| #endif |
| #ifndef LOG_LOCAL2 |
| #define LOG_LOCAL2 0 |
| #endif |
| #ifndef LOG_LOCAL3 |
| #define LOG_LOCAL3 0 |
| #endif |
| #ifndef LOG_LOCAL4 |
| #define LOG_LOCAL4 0 |
| #endif |
| #ifndef LOG_LOCAL5 |
| #define LOG_LOCAL5 0 |
| #endif |
| #ifndef LOG_LOCAL6 |
| #define LOG_LOCAL6 0 |
| #endif |
| #ifndef LOG_LOCAL7 |
| #define LOG_LOCAL7 0 |
| #endif |
| #ifndef LOG_DAEMON |
| #define LOG_DAEMON 0 |
| #endif |
| |
| |
| static void |
| usage(char *prog) |
| { |
| #ifdef WIN32SERVICE |
| printf("\nUsage: %s [-register] [-quiet] [OPTIONS] [LISTENING ADDRESSES]", |
| prog); |
| printf("\n %s [-unregister] [-quiet]", prog); |
| #else |
| printf("\nUsage: %s [OPTIONS] [LISTENING ADDRESSES]", prog); |
| #endif |
| printf("\n"); |
| printf("\n\tVersion: %s\n", netsnmp_get_version()); |
| printf("\tWeb: http://www.net-snmp.org/\n"); |
| printf("\tEmail: net-snmp-coders@lists.sourceforge.net\n"); |
| printf("\n -a\t\t\tlog addresses\n"); |
| printf(" -A\t\t\tappend to the logfile rather than truncating it\n"); |
| printf(" -c FILE[,...]\t\tread FILE(s) as configuration file(s)\n"); |
| printf(" -C\t\t\tdo not read the default configuration files\n"); |
| printf(" -d\t\t\tdump sent and received SNMP packets\n"); |
| printf(" -D[TOKEN[,...]]\tturn on debugging output for the given TOKEN(s)\n" |
| "\t\t\t (try ALL for extremely verbose output)\n" |
| "\t\t\t Don't put space(s) between -D and TOKEN(s).\n"); |
| printf(" -f\t\t\tdo not fork from the shell\n"); |
| #if HAVE_UNISTD_H |
| printf(" -g GID\t\tchange to this numeric gid after opening\n" |
| "\t\t\t transport endpoints\n"); |
| #endif |
| printf(" -h, --help\t\tdisplay this usage message\n"); |
| printf(" -H\t\t\tdisplay configuration file directives understood\n"); |
| printf(" -I [-]INITLIST\tlist of mib modules to initialize (or not)\n"); |
| printf("\t\t\t (run snmpd with -Dmib_init for a list)\n"); |
| printf(" -L <LOGOPTS>\t\ttoggle options controlling where to log to\n"); |
| snmp_log_options_usage("\t", stdout); |
| printf(" -m MIBLIST\t\tuse MIBLIST instead of the default MIB list\n"); |
| printf(" -M DIRLIST\t\tuse DIRLIST as the list of locations\n\t\t\t to look for MIBs\n"); |
| printf(" -p FILE\t\tstore process id in FILE\n"); |
| printf(" -q\t\t\tprint information in a more parsable format\n"); |
| printf(" -r\t\t\tdo not exit if files only accessible to root\n" |
| "\t\t\t cannot be opened\n"); |
| #ifdef WIN32SERVICE |
| printf(" -register\t\tregister as a Windows service\n"); |
| printf(" \t\t\t (followed by -quiet to prevent message popups)\n"); |
| printf(" \t\t\t (followed by the startup parameter list)\n"); |
| printf(" \t\t\t Note that some parameters are not relevant when running as a service\n"); |
| #endif |
| #if HAVE_UNISTD_H |
| printf(" -u UID\t\tchange to this uid (numeric or textual) after\n" |
| "\t\t\t opening transport endpoints\n"); |
| #endif |
| #ifdef WIN32SERVICE |
| printf(" -unregister\t\tunregister as a Windows service\n"); |
| printf(" \t\t\t (followed -quiet to prevent message popups)\n"); |
| #endif |
| printf(" -v, --version\t\tdisplay version information\n"); |
| printf(" -V\t\t\tverbose display\n"); |
| #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) |
| printf(" -x ADDRESS\t\tuse ADDRESS as AgentX address\n"); |
| #endif |
| #ifdef USING_AGENTX_SUBAGENT_MODULE |
| printf(" -X\t\t\trun as an AgentX subagent rather than as an\n" |
| "\t\t\t SNMP master agent\n"); |
| #endif |
| |
| printf("\nDeprecated options:\n"); |
| printf(" -l FILE\t\tuse -Lf <FILE> instead\n"); |
| printf(" -P\t\t\tuse -p instead\n"); |
| printf(" -s\t\t\tuse -Lsd instead\n"); |
| printf(" -S d|i|0-7\t\tuse -Ls <facility> instead\n"); |
| |
| printf("\n"); |
| exit(1); |
| } |
| |
| static void |
| version(void) |
| { |
| printf("\nNET-SNMP version: %s\n", netsnmp_get_version()); |
| printf("Web: http://www.net-snmp.org/\n"); |
| printf("Email: net-snmp-coders@lists.sourceforge.net\n\n"); |
| exit(0); |
| } |
| |
| RETSIGTYPE |
| SnmpdShutDown(int a) |
| { |
| #ifdef WIN32SERVICE |
| extern netsnmp_session *main_session; |
| #endif |
| netsnmp_running = 0; |
| #ifdef WIN32SERVICE |
| /* |
| * In case of windows, select() in receive() function will not return |
| * on signal. Thats why following function is called, which closes the |
| * socket descriptors and causes the select() to return |
| */ |
| snmp_close(main_session); |
| #endif |
| } |
| |
| #ifdef SIGHUP |
| RETSIGTYPE |
| SnmpdReconfig(int a) |
| { |
| reconfig = 1; |
| signal(SIGHUP, SnmpdReconfig); |
| } |
| #endif |
| |
| #ifdef SIGUSR1 |
| extern void dump_registry(void); |
| RETSIGTYPE |
| SnmpdDump(int a) |
| { |
| dump_registry(); |
| signal(SIGUSR1, SnmpdDump); |
| } |
| #endif |
| |
| RETSIGTYPE |
| SnmpdCatchRandomSignal(int a) |
| { |
| /* Disable all logs and log the error via syslog */ |
| snmp_disable_log(); |
| snmp_enable_syslog(); |
| snmp_log(LOG_ERR, "Exiting on signal %d\n", a); |
| snmp_disable_syslog(); |
| exit(1); |
| } |
| |
| static void |
| SnmpTrapNodeDown(void) |
| { |
| send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 2); |
| /* |
| * XXX 2 - Node Down #define it as NODE_DOWN_TRAP |
| */ |
| } |
| |
| /*******************************************************************-o-****** |
| * main - Non Windows |
| * SnmpDaemonMain - Windows to support windows service |
| * |
| * Parameters: |
| * argc |
| * *argv[] |
| * |
| * Returns: |
| * 0 Always succeeds. (?) |
| * |
| * |
| * Setup and start the agent daemon. |
| * |
| * Also successfully EXITs with zero for some options. |
| */ |
| int |
| #ifdef WIN32SERVICE |
| SnmpDaemonMain(int argc, TCHAR * argv[]) |
| #else |
| main(int argc, char *argv[]) |
| #endif |
| { |
| char options[128] = "aAc:CdD::fhHI:l:L:m:M:n:p:P:qrsS:UvV-:Y:"; |
| int arg, i, ret; |
| int dont_fork = 0, do_help = 0; |
| int log_set = 0; |
| int uid = 0, gid = 0; |
| int agent_mode = -1; |
| char *cptr, **argvptr; |
| char *pid_file = NULL; |
| char option_compatability[] = "-Le"; |
| #if HAVE_GETPID |
| int fd; |
| FILE *PID; |
| #endif |
| |
| #ifndef WIN32 |
| /* |
| * close all non-standard file descriptors we may have |
| * inherited from the shell. |
| */ |
| for (i = getdtablesize() - 1; i > 2; --i) { |
| (void) close(i); |
| } |
| #endif /* #WIN32 */ |
| |
| /* |
| * register signals ASAP to prevent default action (usually core) |
| * for signals during startup... |
| */ |
| #ifdef SIGTERM |
| DEBUGMSGTL(("signal", "registering SIGTERM signal handler\n")); |
| signal(SIGTERM, SnmpdShutDown); |
| #endif |
| #ifdef SIGINT |
| DEBUGMSGTL(("signal", "registering SIGINT signal handler\n")); |
| signal(SIGINT, SnmpdShutDown); |
| #endif |
| #ifdef SIGHUP |
| signal(SIGHUP, SIG_IGN); /* do not terminate on early SIGHUP */ |
| #endif |
| #ifdef SIGUSR1 |
| DEBUGMSGTL(("signal", "registering SIGUSR1 signal handler\n")); |
| signal(SIGUSR1, SnmpdDump); |
| #endif |
| #ifdef SIGPIPE |
| DEBUGMSGTL(("signal", "registering SIGPIPE signal handler\n")); |
| signal(SIGPIPE, SIG_IGN); /* 'Inline' failure of wayward readers */ |
| #endif |
| #ifdef SIGXFSZ |
| signal(SIGXFSZ, SnmpdCatchRandomSignal); |
| #endif |
| |
| #ifdef NETSNMP_NO_ROOT_ACCESS |
| /* |
| * Default to no. |
| */ |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); |
| #endif |
| /* |
| * Default to NOT running an AgentX master. |
| */ |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_AGENTX_MASTER, 0); |
| netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_AGENTX_TIMEOUT, -1); |
| netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_AGENTX_RETRIES, -1); |
| |
| netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_CACHE_TIMEOUT, 5); |
| /* |
| * Add some options if they are available. |
| */ |
| #if HAVE_UNISTD_H |
| strcat(options, "g:u:"); |
| #endif |
| #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) |
| strcat(options, "x:"); |
| #endif |
| #ifdef USING_AGENTX_SUBAGENT_MODULE |
| strcat(options, "X"); |
| #endif |
| |
| /* |
| * This is incredibly ugly, but it's probably the simplest way |
| * to handle the old '-L' option as well as the new '-Lx' style |
| */ |
| for (i=0; i<argc; i++) { |
| if (!strcmp(argv[i], "-L")) |
| argv[i] = option_compatability; |
| } |
| |
| #ifdef WIN32 |
| snmp_log_syslogname(app_name_long); |
| #else |
| snmp_log_syslogname(app_name); |
| #endif |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE, app_name); |
| |
| /* |
| * Now process options normally. |
| */ |
| while ((arg = getopt(argc, argv, options)) != EOF) { |
| switch (arg) { |
| case '-': |
| if (strcasecmp(optarg, "help") == 0) { |
| usage(argv[0]); |
| } |
| if (strcasecmp(optarg, "version") == 0) { |
| version(); |
| } |
| |
| handle_long_opt(optarg); |
| break; |
| |
| case 'a': |
| log_addresses++; |
| break; |
| |
| case 'A': |
| netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPEND_LOGFILES, 1); |
| break; |
| |
| case 'c': |
| if (optarg != NULL) { |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_OPTIONALCONFIG, optarg); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'C': |
| netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1); |
| break; |
| |
| case 'd': |
| snmp_set_dump_packet(++snmp_dump_packet); |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_VERBOSE, 1); |
| break; |
| |
| case 'D': |
| debug_register_tokens(optarg); |
| snmp_set_do_debugging(1); |
| break; |
| |
| case 'f': |
| dont_fork = 1; |
| break; |
| |
| #if HAVE_UNISTD_H |
| case 'g': |
| if (optarg != NULL) { |
| char *ecp; |
| int gid; |
| |
| gid = strtoul(optarg, &ecp, 10); |
| if (*ecp) { |
| #if HAVE_GETPWNAM && HAVE_PWD_H |
| struct group *info; |
| info = getgrnam(optarg); |
| if (info) { |
| gid = info->gr_gid; |
| } else { |
| #endif |
| fprintf(stderr, "Bad group id: %s\n", optarg); |
| exit(1); |
| #if HAVE_GETPWNAM && HAVE_PWD_H |
| } |
| #endif |
| } |
| netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_GROUPID, gid); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| #endif |
| |
| case 'h': |
| usage(argv[0]); |
| break; |
| |
| case 'H': |
| do_help = 1; |
| break; |
| |
| case 'I': |
| if (optarg != NULL) { |
| add_to_init_list(optarg); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'l': |
| printf("Warning: -l option is deprecated, use -Lf <file> instead\n"); |
| if (optarg != NULL) { |
| if (strlen(optarg) > PATH_MAX) { |
| fprintf(stderr, |
| "%s: logfile path too long (limit %d chars)\n", |
| argv[0], PATH_MAX); |
| exit(1); |
| } |
| snmp_enable_filelog(optarg, |
| netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPEND_LOGFILES)); |
| log_set = 1; |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'L': |
| if (snmp_log_options( optarg, argc, argv ) < 0 ) { |
| usage(argv[0]); |
| } |
| log_set = 1; |
| break; |
| |
| case 'm': |
| if (optarg != NULL) { |
| setenv("MIBS", optarg, 1); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'M': |
| if (optarg != NULL) { |
| setenv("MIBDIRS", optarg, 1); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'n': |
| if (optarg != NULL) { |
| app_name = optarg; |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE, app_name); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'P': |
| printf("Warning: -P option is deprecated, use -p instead\n"); |
| case 'p': |
| if (optarg != NULL) { |
| pid_file = optarg; |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'q': |
| snmp_set_quick_print(1); |
| break; |
| |
| case 'r': |
| netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS); |
| break; |
| |
| case 's': |
| printf("Warning: -s option is deprecated, use -Lsd instead\n"); |
| snmp_enable_syslog(); |
| log_set = 1; |
| break; |
| |
| case 'S': |
| printf("Warning: -S option is deprecated, use -Ls <facility> instead\n"); |
| if (optarg != NULL) { |
| switch (*optarg) { |
| case 'd': |
| case 'D': |
| Facility = LOG_DAEMON; |
| break; |
| case 'i': |
| case 'I': |
| Facility = LOG_INFO; |
| break; |
| case '0': |
| Facility = LOG_LOCAL0; |
| break; |
| case '1': |
| Facility = LOG_LOCAL1; |
| break; |
| case '2': |
| Facility = LOG_LOCAL2; |
| break; |
| case '3': |
| Facility = LOG_LOCAL3; |
| break; |
| case '4': |
| Facility = LOG_LOCAL4; |
| break; |
| case '5': |
| Facility = LOG_LOCAL5; |
| break; |
| case '6': |
| Facility = LOG_LOCAL6; |
| break; |
| case '7': |
| Facility = LOG_LOCAL7; |
| break; |
| default: |
| fprintf(stderr, "invalid syslog facility: -S%c\n",*optarg); |
| usage(argv[0]); |
| } |
| snmp_enable_syslog_ident(snmp_log_syslogname(NULL), Facility); |
| log_set = 1; |
| } else { |
| fprintf(stderr, "no syslog facility specified\n"); |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'U': |
| netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_LEAVE_PIDFILE); |
| break; |
| |
| #if HAVE_UNISTD_H |
| case 'u': |
| if (optarg != NULL) { |
| char *ecp; |
| int uid; |
| |
| uid = strtoul(optarg, &ecp, 10); |
| if (*ecp) { |
| #if HAVE_GETPWNAM && HAVE_PWD_H |
| struct passwd *info; |
| info = getpwnam(optarg); |
| if (info) { |
| uid = info->pw_uid; |
| } else { |
| #endif |
| fprintf(stderr, "Bad user id: %s\n", optarg); |
| exit(1); |
| #if HAVE_GETPWNAM && HAVE_PWD_H |
| } |
| #endif |
| } |
| netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_USERID, uid); |
| } else { |
| usage(argv[0]); |
| } |
| break; |
| #endif |
| |
| case 'v': |
| version(); |
| |
| case 'V': |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_VERBOSE, 1); |
| break; |
| |
| #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) |
| case 'x': |
| if (optarg != NULL) { |
| netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_X_SOCKET, optarg); |
| } else { |
| usage(argv[0]); |
| } |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_AGENTX_MASTER, 1); |
| break; |
| #endif |
| |
| case 'X': |
| #if defined(USING_AGENTX_SUBAGENT_MODULE) |
| agent_mode = SUB_AGENT; |
| #else |
| fprintf(stderr, "%s: Illegal argument -X:" |
| "AgentX support not compiled in.\n", argv[0]); |
| usage(argv[0]); |
| exit(1); |
| #endif |
| break; |
| |
| case 'Y': |
| netsnmp_config_remember(optarg); |
| break; |
| |
| default: |
| usage(argv[0]); |
| break; |
| } |
| } |
| |
| if (do_help) { |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); |
| init_agent(app_name); /* register our .conf handlers */ |
| init_mib_modules(); |
| init_snmp(app_name); |
| fprintf(stderr, "Configuration directives understood:\n"); |
| read_config_print_usage(" "); |
| exit(0); |
| } |
| |
| if (optind < argc) { |
| /* |
| * There are optional transport addresses on the command line. |
| */ |
| DEBUGMSGTL(("snmpd/main", "optind %d, argc %d\n", optind, argc)); |
| for (i = optind; i < argc; i++) { |
| char *c, *astring; |
| if ((c = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_PORTS))) { |
| astring = malloc(strlen(c) + 2 + strlen(argv[i])); |
| if (astring == NULL) { |
| fprintf(stderr, "malloc failure processing argv[%d]\n", i); |
| exit(1); |
| } |
| sprintf(astring, "%s,%s", c, argv[i]); |
| netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_PORTS, astring); |
| SNMP_FREE(astring); |
| } else { |
| netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_PORTS, argv[i]); |
| } |
| } |
| DEBUGMSGTL(("snmpd/main", "port spec: %s\n", |
| netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_PORTS))); |
| } |
| |
| #ifdef NETSNMP_LOGFILE |
| if (0 == log_set) |
| snmp_enable_filelog(NETSNMP_LOGFILE, |
| netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPEND_LOGFILES)); |
| #endif |
| |
| /* |
| * Initialize a argv set to the current for restarting the agent. |
| */ |
| argvrestartp = (char **)malloc((argc + 2) * sizeof(char *)); |
| argvptr = argvrestartp; |
| for (i = 0, ret = 1; i < argc; i++) { |
| ret += strlen(argv[i]) + 1; |
| } |
| argvrestart = (char *) malloc(ret); |
| argvrestartname = (char *) malloc(strlen(argv[0]) + 1); |
| if (!argvrestartp || !argvrestart || !argvrestartname) { |
| fprintf(stderr, "malloc failure processing argvrestart\n"); |
| exit(1); |
| } |
| strcpy(argvrestartname, argv[0]); |
| if (agent_mode == -1) { |
| if (strstr(argvrestartname, "agentxd") != NULL) { |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_ROLE, SUB_AGENT); |
| } else { |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_ROLE, MASTER_AGENT); |
| } |
| } else { |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_ROLE, agent_mode); |
| } |
| |
| for (cptr = argvrestart, i = 0; i < argc; i++) { |
| strcpy(cptr, argv[i]); |
| *(argvptr++) = cptr; |
| cptr += strlen(argv[i]) + 1; |
| } |
| *cptr = 0; |
| *argvptr = NULL; |
| |
| #ifdef BUFSIZ |
| setvbuf(stdout, NULL, _IOLBF, BUFSIZ); |
| #endif |
| /* |
| * Initialize the world. Detach from the shell. Create initial user. |
| */ |
| if(!dont_fork) { |
| int quit = ! netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_QUIT_IMMEDIATELY); |
| ret = netsnmp_daemonize(quit, snmp_stderrlog_status()); |
| /* |
| * xxx-rks: do we care if fork fails? I think we should... |
| */ |
| if(ret != 0) |
| Exit(1); /* Exit logs exit val for us */ |
| } |
| |
| SOCK_STARTUP; |
| if (init_agent(app_name) != 0) { |
| snmp_log(LOG_ERR, "Agent initialization failed\n"); |
| exit(1); |
| } |
| init_mib_modules(); |
| |
| /* |
| * start library |
| */ |
| init_snmp(app_name); |
| |
| if ((ret = init_master_agent()) != 0) { |
| /* |
| * Some error opening one of the specified agent transports. |
| */ |
| Exit(1); /* Exit logs exit val for us */ |
| } |
| |
| #if HAVE_GETPID |
| if (pid_file != NULL) { |
| /* |
| * unlink the pid_file, if it exists, prior to open. Without |
| * doing this the open will fail if the user specified pid_file |
| * already exists. |
| */ |
| unlink(pid_file); |
| fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, 0600); |
| if (fd == -1) { |
| snmp_log_perror(pid_file); |
| if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { |
| exit(1); |
| } |
| } else { |
| if ((PID = fdopen(fd, "w")) == NULL) { |
| snmp_log_perror(pid_file); |
| exit(1); |
| } else { |
| fprintf(PID, "%d\n", (int) getpid()); |
| fclose(PID); |
| } |
| #ifndef _MSC_VER |
| /* The sequence open()/fdopen()/fclose()/close() makes MSVC crash, |
| hence skip the close() call when using the MSVC runtime. */ |
| close(fd); |
| #endif |
| } |
| } |
| #endif |
| |
| #if HAVE_UNISTD_H |
| { |
| const char *const persistent_dir = get_persistent_directory(); |
| mkdirhier( persistent_dir, NETSNMP_AGENT_DIRECTORY_MODE, 0 ); |
| |
| uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_USERID); |
| gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_GROUPID); |
| |
| #ifdef HAVE_CHOWN |
| if ( uid != 0 || gid != 0 ) |
| chown( persistent_dir, uid, gid ); |
| #endif |
| } |
| |
| #ifdef HAVE_SETGID |
| if ((gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_GROUPID)) > 0) { |
| DEBUGMSGTL(("snmpd/main", "Changing gid to %d.\n", gid)); |
| if (setgid(gid) == -1 |
| #ifdef HAVE_SETGROUPS |
| || setgroups(1, (gid_t *)&gid) == -1 |
| #endif |
| ) { |
| snmp_log_perror("setgid failed"); |
| if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { |
| exit(1); |
| } |
| } |
| } |
| #endif |
| #ifdef HAVE_SETUID |
| if ((uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_USERID)) > 0) { |
| DEBUGMSGTL(("snmpd/main", "Changing uid to %d.\n", uid)); |
| if (setuid(uid) == -1) { |
| snmp_log_perror("setuid failed"); |
| if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { |
| exit(1); |
| } |
| } |
| } |
| #endif |
| #endif |
| |
| /* |
| * Store persistent data immediately in case we crash later. |
| */ |
| snmp_store(app_name); |
| |
| #ifdef SIGHUP |
| DEBUGMSGTL(("signal", "registering SIGHUP signal handler\n")); |
| signal(SIGHUP, SnmpdReconfig); |
| #endif |
| |
| /* |
| * Send coldstart trap if possible. |
| */ |
| send_easy_trap(0, 0); |
| |
| /* |
| * We're up, log our version number. |
| */ |
| snmp_log(LOG_INFO, "NET-SNMP version %s\n", netsnmp_get_version()); |
| #ifdef WIN32SERVICE |
| agent_status = AGENT_RUNNING; |
| #endif |
| netsnmp_addrcache_initialise(); |
| |
| /* |
| * Forever monitor the dest_port for incoming PDUs. |
| */ |
| DEBUGMSGTL(("snmpd/main", "We're up. Starting to process data.\n")); |
| if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) |
| receive(); |
| DEBUGMSGTL(("snmpd/main", "sending shutdown trap\n")); |
| SnmpTrapNodeDown(); |
| DEBUGMSGTL(("snmpd/main", "Bye...\n")); |
| snmp_shutdown(app_name); |
| shutdown_master_agent(); |
| shutdown_agent(); |
| |
| if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_LEAVE_PIDFILE) && |
| (pid_file != NULL)) { |
| unlink(pid_file); |
| } |
| #ifdef WIN32SERVICE |
| agent_status = AGENT_STOPPED; |
| #endif |
| |
| SNMP_FREE(argvrestartname); |
| SNMP_FREE(argvrestart); |
| SNMP_FREE(argvrestartp); |
| SOCK_CLEANUP; |
| return 0; |
| } /* End main() -- snmpd */ |
| |
| #if defined(WIN32) |
| |
| #include <process.h> |
| #include <net-snmp/library/snmp_assert.h> |
| |
| static unsigned s_threadid; |
| HANDLE s_thread_handle; |
| |
| static unsigned __stdcall wait_for_stdin(void* arg) |
| { |
| if (getc(stdin) != EOF) |
| netsnmp_running = 0; |
| return 0; |
| } |
| |
| static void create_stdin_waiter_thread(void) |
| { |
| netsnmp_assert(s_thread_handle == 0); |
| s_thread_handle = (HANDLE)_beginthreadex(0, 0, wait_for_stdin, 0, 0, &s_threadid); |
| netsnmp_assert(s_thread_handle != 0); |
| } |
| |
| static void join_stdin_waiter_thread(void) |
| { |
| int result; |
| |
| netsnmp_assert(s_thread_handle != 0); |
| result = WaitForSingleObject(s_thread_handle, 1000); |
| netsnmp_assert(result != WAIT_TIMEOUT); |
| CloseHandle(s_thread_handle); |
| s_thread_handle = 0; |
| } |
| #endif |
| |
| /*******************************************************************-o-****** |
| * receive |
| * |
| * Parameters: |
| * |
| * Returns: |
| * 0 On success. |
| * -1 System error. |
| * |
| * Infinite while-loop which monitors incoming messges for the agent. |
| * Invoke the established message handlers for incoming messages on a per |
| * port basis. Handle timeouts. |
| */ |
| static int |
| receive(void) |
| { |
| int numfds; |
| fd_set readfds, writefds, exceptfds; |
| struct timeval timeout, *tvp = &timeout; |
| int count, block, i; |
| #ifdef USING_SMUX_MODULE |
| int sd; |
| #endif /* USING_SMUX_MODULE */ |
| |
| /* |
| * ignore early sighup during startup |
| */ |
| reconfig = 0; |
| |
| #if defined(WIN32) |
| create_stdin_waiter_thread(); |
| #endif |
| |
| /* |
| * Loop-forever: execute message handlers for sockets with data |
| */ |
| while (netsnmp_running) { |
| if (reconfig) { |
| #if HAVE_SIGHOLD |
| sighold(SIGHUP); |
| #endif |
| reconfig = 0; |
| snmp_log(LOG_INFO, "Reconfiguring daemon\n"); |
| /* Stop and restart logging. This allows logfiles to be |
| rotated etc. */ |
| netsnmp_logging_restart(); |
| snmp_log(LOG_INFO, "NET-SNMP version %s restarted\n", |
| netsnmp_get_version()); |
| update_config(); |
| send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 3); |
| #if HAVE_SIGHOLD |
| sigrelse(SIGHUP); |
| #endif |
| } |
| |
| /* |
| * default to sleeping for a really long time. INT_MAX |
| * should be sufficient (eg we don't care if time_t is |
| * a long that's bigger than an int). |
| */ |
| tvp = &timeout; |
| tvp->tv_sec = INT_MAX; |
| tvp->tv_usec = 0; |
| |
| numfds = 0; |
| FD_ZERO(&readfds); |
| FD_ZERO(&writefds); |
| FD_ZERO(&exceptfds); |
| block = 0; |
| snmp_select_info(&numfds, &readfds, tvp, &block); |
| if (block == 1) { |
| tvp = NULL; /* block without timeout */ |
| } |
| |
| #ifdef USING_SMUX_MODULE |
| if (smux_listen_sd >= 0) { |
| FD_SET(smux_listen_sd, &readfds); |
| numfds = |
| smux_listen_sd >= numfds ? smux_listen_sd + 1 : numfds; |
| |
| for (i = 0; i < smux_snmp_select_list_get_length(); i++) { |
| sd = smux_snmp_select_list_get_SD_from_List(i); |
| if (sd != 0) |
| { |
| FD_SET(sd, &readfds); |
| numfds = sd >= numfds ? sd + 1 : numfds; |
| } |
| } |
| } |
| #endif /* USING_SMUX_MODULE */ |
| |
| netsnmp_external_event_info(&numfds, &readfds, &writefds, &exceptfds); |
| |
| reselect: |
| for (i = 0; i < NUM_EXTERNAL_SIGS; i++) { |
| if (external_signal_scheduled[i]) { |
| external_signal_scheduled[i]--; |
| external_signal_handler[i](i); |
| } |
| } |
| |
| DEBUGMSGTL(("snmpd/select", "select( numfds=%d, ..., tvp=%p)\n", |
| numfds, tvp)); |
| if(tvp) |
| DEBUGMSGTL(("timer", "tvp %ld.%ld\n", tvp->tv_sec, tvp->tv_usec)); |
| count = select(numfds, &readfds, &writefds, &exceptfds, tvp); |
| DEBUGMSGTL(("snmpd/select", "returned, count = %d\n", count)); |
| |
| if (count > 0) { |
| |
| #ifdef USING_SMUX_MODULE |
| /* |
| * handle the SMUX sd's |
| */ |
| if (smux_listen_sd >= 0) { |
| for (i = 0; i < smux_snmp_select_list_get_length(); i++) { |
| sd = smux_snmp_select_list_get_SD_from_List(i); |
| if (FD_ISSET(sd, &readfds)) { |
| if (smux_process(sd) < 0) { |
| smux_snmp_select_list_del(sd); |
| } |
| } |
| } |
| /* |
| * new connection |
| */ |
| if (FD_ISSET(smux_listen_sd, &readfds)) { |
| if ((sd = smux_accept(smux_listen_sd)) >= 0) { |
| smux_snmp_select_list_add(sd); |
| } |
| } |
| } |
| |
| #endif /* USING_SMUX_MODULE */ |
| netsnmp_dispatch_external_events(&count, &readfds, |
| &writefds, &exceptfds); |
| /* If there are still events leftover, process them */ |
| if (count > 0) { |
| snmp_read(&readfds); |
| } |
| } else |
| switch (count) { |
| case 0: |
| snmp_timeout(); |
| break; |
| case -1: |
| DEBUGMSGTL(("snmpd/select", " errno = %d\n", errno)); |
| if (errno == EINTR) { |
| /* |
| * likely that we got a signal. Check our special signal |
| * flags before retrying select. |
| */ |
| if (netsnmp_running && !reconfig) { |
| goto reselect; |
| } |
| continue; |
| } else { |
| snmp_log_perror("select"); |
| } |
| return -1; |
| default: |
| snmp_log(LOG_ERR, "select returned %d\n", count); |
| return -1; |
| } /* endif -- count>0 */ |
| |
| /* |
| * run requested alarms |
| */ |
| run_alarms(); |
| |
| netsnmp_check_outstanding_agent_requests(); |
| |
| } /* endwhile */ |
| |
| #if defined(WIN32) |
| join_stdin_waiter_thread(); |
| #endif |
| |
| snmp_log(LOG_INFO, "Received TERM or STOP signal... shutting down...\n"); |
| return 0; |
| |
| } /* end receive() */ |
| |
| |
| |
| /*******************************************************************-o-****** |
| * snmp_input |
| * |
| * Parameters: |
| * op |
| * *session |
| * requid |
| * *pdu |
| * *magic |
| * |
| * Returns: |
| * 1 On success -OR- |
| * Passes through Return from alarmGetResponse() when |
| * USING_V2PARTY_ALARM_MODULE is defined. |
| * |
| * Call-back function to manage responses to traps (informs) and alarms. |
| * Not used by the agent to process other Response PDUs. |
| */ |
| int |
| snmp_input(int op, |
| netsnmp_session * session, |
| int reqid, netsnmp_pdu *pdu, void *magic) |
| { |
| struct get_req_state *state = (struct get_req_state *) magic; |
| |
| if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) { |
| if (pdu->command == SNMP_MSG_GET) { |
| if (state->type == EVENT_GET_REQ) { |
| /* |
| * this is just the ack to our inform pdu |
| */ |
| return 1; |
| } |
| } |
| } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { |
| if (state->type == ALARM_GET_REQ) { |
| /* |
| * Need a mechanism to replace obsolete SNMPv2p alarm |
| */ |
| } |
| } |
| return 1; |
| |
| } /* end snmp_input() */ |
| |
| |
| |
| /* |
| * Windows Service Related functions |
| */ |
| #ifdef WIN32SERVICE |
| /************************************************************ |
| * main function for Windows |
| * Parse command line arguments for startup options, |
| * to start as service or console mode application in windows. |
| * Invokes appropriate startup functions depending on the |
| * parameters passed |
| *************************************************************/ |
| int |
| __cdecl |
| _tmain(int argc, TCHAR * argv[]) |
| { |
| /* |
| * Define Service Name and Description, which appears in windows SCM |
| */ |
| LPCTSTR lpszServiceName = app_name_long; /* Service Registry Name */ |
| LPCTSTR lpszServiceDisplayName = _T("Net-SNMP Agent"); /* Display Name */ |
| LPCTSTR lpszServiceDescription = |
| #ifdef IFDESCR |
| _T("SNMPv2c / SNMPv3 command responder from Net-SNMP. Supports MIB objects for IP,ICMP,TCP,UDP, and network interface sub-layers."); |
| #else |
| _T("SNMPv2c / SNMPv3 command responder from Net-SNMP"); |
| #endif |
| InputParams InputOptions; |
| |
| |
| int nRunType = RUN_AS_CONSOLE; |
| int quiet = 0; |
| |
| nRunType = ParseCmdLineForServiceOption(argc, argv, &quiet); |
| |
| switch (nRunType) { |
| case REGISTER_SERVICE: |
| /* |
| * Register As service |
| */ |
| InputOptions.Argc = argc; |
| InputOptions.Argv = argv; |
| exit (RegisterService(lpszServiceName, |
| lpszServiceDisplayName, |
| lpszServiceDescription, &InputOptions, quiet)); |
| break; |
| case UN_REGISTER_SERVICE: |
| /* |
| * Unregister service |
| */ |
| exit (UnregisterService(lpszServiceName, quiet)); |
| break; |
| case RUN_AS_SERVICE: |
| /* |
| * Run as service |
| */ |
| /* |
| * Register Stop Function |
| */ |
| RegisterStopFunction(StopSnmpAgent); |
| return RunAsService(SnmpDaemonMain); |
| break; |
| default: |
| /* |
| * Run in console mode |
| */ |
| return SnmpDaemonMain(argc, argv); |
| break; |
| } |
| } |
| |
| /* |
| * To stop Snmp Agent daemon |
| * This portion is still not working |
| */ |
| void |
| StopSnmpAgent(void) |
| { |
| /* |
| * Shut Down Agent |
| */ |
| SnmpdShutDown(1); |
| |
| /* |
| * Wait till agent is completely stopped |
| */ |
| |
| while (agent_status != AGENT_STOPPED) { |
| Sleep(100); |
| } |
| } |
| |
| #endif /*WIN32SERVICE*/ |