text: /*	$OpenBSD: snmpctl.c,v 1.13 2011/03/16 15:30:35 reyk Exp $	*/
  
  /*
   * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net>
   * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
   * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
   * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
   *
   * Permission to use, copy, modify, and distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, 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.
   */
  
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <sys/queue.h>
  #include <sys/un.h>
  #include <sys/tree.h>
  
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <net/if.h>
  #include <net/if_media.h>
  #include <net/if_types.h>
  
  #include <err.h>
  #include <errno.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <event.h>
  
  #include "snmpd.h"
  #include "snmp.h"
  #include "parser.h"
  #include "mib.h"
  
  __dead void	 usage(void);
  
  struct imsgname {
  	int type;
  	char *name;
  	void (*func)(struct imsg *);
  };
  
  void		 show_mib(void);
  struct imsgname *monitor_lookup(u_int8_t);
  void		 monitor_id(struct imsg *);
  int		 monitor(struct imsg *);
  
  struct imsgname imsgs[] = {
  	{ 0,				NULL,			NULL }
  };
  struct imsgname imsgunknown = {
  	-1,				"<unknown>",		NULL
  };
  
  struct imsgbuf	*ibuf;
  struct snmpd	*env;
  struct oid	 mib_tree[] = MIB_TREE;
  
  __dead void
  usage(void)
  {
  	extern char *__progname;
  
  	fprintf(stderr, "usage: %s [-n] [-s socket] command [arg ...]\n", __progname);
  	exit(1);
  }
  
  int
  main(int argc, char *argv[])
  {
  	struct sockaddr_un	 sun;
  	struct parse_result	*res;
  	struct imsg		 imsg;
  	int			 ctl_sock;
  	int			 done = 0;
  	int			 n;
  	int			 ch;
  	const char		*sock = SNMPD_SOCKET;
  
  	if ((env = calloc(1, sizeof(struct snmpd *))) == NULL)
  		err(1, "calloc");
  	gettimeofday(&env->sc_starttime, NULL);
  
  	while ((ch = getopt(argc, argv, "ns:")) != -1) {
  		switch (ch) {
  		case 'n':
  			env->sc_flags |= SNMPD_F_NONAMES;
  			break;
  		case 's':
  			sock = optarg;
  			break;
  		default:
  			usage();
  			/* NOTREACHED */
  		}
  	}
  	argc -= optind;
  	argv += optind;
  
  	smi_init();
  
  	/* parse options */
  	if ((res = parse(argc, argv)) == NULL)
  		exit(1);
  
  	switch (res->action) {
  	case NONE:
  		usage();
  		break;
  	case SHOW_MIB:
  		show_mib();
  		break;
  	default:
  		goto connect;
  	}
  
  	free(env);
  	return (0);
  
  connect:
  /* connect to snmpd control socket */
  if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
  	err(1, "socket");
  
  	bzero(&sun, sizeof(sun));
  	sun.sun_family = AF_UNIX;
  	strlcpy(sun.sun_path, sock, sizeof(sun.sun_path));
   reconnect:
  	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
  		/* Keep retrying if running in monitor mode */
  		if (res->action == MONITOR &&
  		    (errno == ENOENT || errno == ECONNREFUSED)) {
  			usleep(100);
  			goto reconnect;
  		}
  		err(1, "connect: %s", sock);
  	}
  
  	if (res->ibuf != NULL) {
  		ibuf = res->ibuf;
  		ibuf->fd = ibuf->w.fd = ctl_sock;
  	} else {
  		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
  			err(1, "malloc");
  		imsg_init(ibuf, ctl_sock);
  	}
  	done = 0;
  
  	/* process user request */
  	switch (res->action) {
  	case MONITOR:
  		imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
  		break;
  	case NONE:
  	case SHOW_MIB:
  		break;
  	case TRAP:
  		imsg_compose(ibuf, IMSG_SNMP_END, 0, 0, -1, NULL, 0);
  		done = 1;
  		break;
  	}
  
  	while (ibuf->w.queued)
  		if (msgbuf_write(&ibuf->w) < 0)
  			err(1, "write error");
  
  	while (!done) {
  		if ((n = imsg_read(ibuf)) == -1)
  			errx(1, "imsg_read error");
  		if (n == 0)
  			errx(1, "pipe closed");
  
  		while (!done) {
  			if ((n = imsg_get(ibuf, &imsg)) == -1)
  				errx(1, "imsg_get error");
  			if (n == 0)
  				break;
  			switch (res->action) {
  			case MONITOR:
  				done = monitor(&imsg);
  				break;
  			case TRAP:
  				break;
  			case NONE:
  			case SHOW_MIB:
  				break;
  			}
  			imsg_free(&imsg);
  		}
  	}
  	close(ctl_sock);
  	free(ibuf);
  
  	return (0);
  }
  
  void
  mib_init(void)
  {
  	/*
  	 * MIB declarations (to register the OID names)
  	 */
  	smi_mibtree(mib_tree);
  }
  
  void
  show_mib(void)
  {
  	struct oid	*oid;
  
  	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
  		char	 buf[BUFSIZ];
  		smi_oidstring(&oid->o_id, buf, sizeof(buf));
  		printf("%s\n", buf);
  	}
  }
  
  struct imsgname *
  monitor_lookup(u_int8_t type)
  {
  	int i;
  
  	for (i = 0; imsgs[i].name != NULL; i++)
  		if (imsgs[i].type == type)
  			return (&imsgs[i]);
  	return (&imsgunknown);
  }
  
  int
  monitor(struct imsg *imsg)
  {
  	time_t			 now;
  	int			 done = 0;
  	struct imsgname		*imn;
  
  	now = time(NULL);
  
  	imn = monitor_lookup(imsg->hdr.type);
  	printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
  	    imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
  	printf("\ttimestamp: %u, %s", now, ctime(&now));
  	if (imn->type == -1)
  		done = 1;
  	if (imn->func != NULL)
  		(*imn->func)(imsg);
  
  	return (done);
  }
  