lldpd_main(int argc, char *argv[], char *envp[])
{
	struct lldpd *cfg;
	struct lldpd_chassis *lchassis;
	int ch, debug = 0;
#ifdef USE_SNMP
	int snmp = 0;
	const char *agentx = NULL;	/* AgentX socket */
#endif
	const char *ctlname = NULL;
	char *mgmtp = NULL;
	char *cidp = NULL;
	char *interfaces = NULL;
	/* We do not want more options here. Please add them in lldpcli instead
	 * unless there is a very good reason. Most command-line options will
	 * get deprecated at some point. */
	char *popt, opts[] =
		"H:vhkrdD:xX:m:u:4:6:I:C:p:M:P:S:iL:@                    ";
	int i, found, advertise_version = 1;
#ifdef ENABLE_LLDPMED
	int lldpmed = 0, noinventory = 0;
	int enable_fast_start = 1;
#endif
	char *descr_override = NULL;
	char *platform_override = NULL;
	char *lsb_release = NULL;
	const char *lldpcli = LLDPCLI_PATH;
	int smart = 15;
	int receiveonly = 0;
	int ctl;

#ifdef ENABLE_PRIVSEP
	/* Non privileged user */
	struct passwd *user;
	struct group *group;
	uid_t uid;
	gid_t gid;
#endif

	saved_argv = argv;

#if HAVE_SETPROCTITLE_INIT
	setproctitle_init(argc, argv, envp);
#endif

	/*
	 * Get and parse command line options
	 */
	if ((popt = strchr(opts, '@')) != NULL) {
		for (i=0;
		     protos[i].mode != 0 && *popt != '\0';
		     i++)
			*(popt++) = protos[i].arg;
		*popt = '\0';
	}
	while ((ch = getopt(argc, argv, opts)) != -1) {
		switch (ch) {
		case 'h':
			usage();
			break;
		case 'v':
			fprintf(stdout, "%s\n", PACKAGE_VERSION);
			exit(0);
			break;
		case 'd':
			debug++;
			break;
		case 'D':
			log_accept(optarg);
			break;
		case 'r':
			receiveonly = 1;
			break;
		case 'm':
			if (mgmtp) {
				fprintf(stderr, "-m can only be used once\n");
				usage();
			}
			mgmtp = strdup(optarg);
			break;
		case 'u':
			if (ctlname) {
				fprintf(stderr, "-u can only be used once\n");
				usage();
			}
			ctlname = optarg;
			break;
		case 'I':
			if (interfaces) {
				fprintf(stderr, "-I can only be used once\n");
				usage();
			}
			interfaces = strdup(optarg);
			break;
		case 'C':
			if (cidp) {
				fprintf(stderr, "-C can only be used once\n");
				usage();
			}
			cidp = strdup(optarg);
			break;
		case 'L':
			if (strlen(optarg)) lldpcli = optarg;
			else lldpcli = NULL;
			break;
		case 'k':
			advertise_version = 0;
			break;
#ifdef ENABLE_LLDPMED
		case 'M':
			lldpmed = atoi(optarg);
			if ((lldpmed < 1) || (lldpmed > 4)) {
				fprintf(stderr, "-M requires an argument between 1 and 4\n");
				usage();
			}
			break;
		case 'i':
			noinventory = 1;
			break;
#else
		case 'M':
		case 'i':
			fprintf(stderr, "LLDP-MED support is not built-in\n");
			usage();
			break;
#endif
#ifdef USE_SNMP
		case 'x':
			snmp = 1;
			break;
		case 'X':
			if (agentx) {
				fprintf(stderr, "-X can only be used once\n");
				usage();
			}
			snmp = 1;
			agentx = optarg;
			break;
#else
		case 'x':
		case 'X':
			fprintf(stderr, "SNMP support is not built-in\n");
			usage();
#endif
			break;
                case 'S':
			if (descr_override) {
				fprintf(stderr, "-S can only be used once\n");
				usage();
			}
                        descr_override = strdup(optarg);
                        break;
		case 'P':
			if (platform_override) {
				fprintf(stderr, "-P can only be used once\n");
				usage();
			}
			platform_override = strdup(optarg);
			break;
		case 'H':
			smart = atoi(optarg);
			break;
		default:
			found = 0;
			for (i=0; protos[i].mode != 0; i++) {
				if (ch == protos[i].arg) {
					found = 1;
					protos[i].enabled++;
				}
			}
			if (!found)
				usage();
		}
	}

	if (ctlname == NULL) ctlname = LLDPD_CTL_SOCKET;

	/* Set correct smart mode */
	for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
	if (filters[i].a == -1) {
		fprintf(stderr, "Incorrect mode for -H\n");
		usage();
	}
	smart = filters[i].b;

	log_init(debug, __progname);
	tzset();		/* Get timezone info before chroot */

	log_debug("main", "lldpd " PACKAGE_VERSION " starting...");

	/* Grab uid and gid to use for priv sep */
#ifdef ENABLE_PRIVSEP
	if ((user = getpwnam(PRIVSEP_USER)) == NULL)
		fatal("main", "no " PRIVSEP_USER " user for privilege separation");
	uid = user->pw_uid;
	if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
		fatal("main", "no " PRIVSEP_GROUP " group for privilege separation");
	gid = group->gr_gid;
#endif

	/* Create and setup socket */
	int retry = 1;
	log_debug("main", "creating control socket");
	while ((ctl = ctl_create(ctlname)) == -1) {
		if (retry-- && errno == EADDRINUSE) {
			/* Check if a daemon is really listening */
			int tfd;
			log_info("main", "unable to create control socket because it already exists");
			log_info("main", "check if another instance is running");
			if ((tfd = ctl_connect(ctlname)) != -1) {
				/* Another instance is running */
				close(tfd);
				log_warnx("main", "another instance is running, please stop it");
				fatalx("main", "giving up");
			} else if (errno == ECONNREFUSED) {
				/* Nobody is listening */
				log_info("main", "old control socket is present, clean it");
				ctl_cleanup(ctlname);
				continue;
			}
			log_warn("main", "cannot determine if another daemon is already running");
			fatalx("main", "giving up");
		}
		log_warn("main", "unable to create control socket");
		fatalx("main", "giving up");
	}
#ifdef ENABLE_PRIVSEP
	if (chown(ctlname, uid, gid) == -1)
		log_warn("main", "unable to chown control socket");
	if (chmod(ctlname,
		S_IRUSR | S_IWUSR | S_IXUSR |
		S_IRGRP | S_IWGRP | S_IXGRP) == -1)
		log_warn("main", "unable to chmod control socket");
#endif

	/* Disable SIGPIPE */
	signal(SIGPIPE, SIG_IGN);

	/* Disable SIGHUP, until handlers are installed */
	signal(SIGHUP, SIG_IGN);

	/* Configuration with lldpcli */
	if (lldpcli) {
		log_debug("main", "invoking lldpcli for configuration");
		if (lldpd_configure(debug, lldpcli, ctlname) == -1)
			fatal("main", "unable to spawn lldpcli");
	}

	/* Daemonization, unless started by upstart, systemd or launchd or debug */
#ifndef HOST_OS_OSX
	if (!lldpd_started_by_upstart() && !lldpd_started_by_systemd() &&
	    !debug) {
		int pid;
		char *spid;
		log_debug("main", "daemonize");
		if (daemon(0, 0) != 0)
			fatal("main", "failed to detach daemon");
		if ((pid = open(LLDPD_PID_FILE,
			    O_TRUNC | O_CREAT | O_WRONLY, 0666)) == -1)
			fatal("main", "unable to open pid file " LLDPD_PID_FILE);
		if (asprintf(&spid, "%d\n", getpid()) == -1)
			fatal("main", "unable to create pid file " LLDPD_PID_FILE);
		if (write(pid, spid, strlen(spid)) == -1)
			fatal("main", "unable to write pid file " LLDPD_PID_FILE);
		free(spid);
		close(pid);
	}
#endif

	/* Try to read system information from /etc/os-release if possible.
	   Fall back to lsb_release for compatibility. */
	log_debug("main", "get OS/LSB release information");
	lsb_release = lldpd_get_os_release();
	if (!lsb_release) {
		lsb_release = lldpd_get_lsb_release();
	}

	log_debug("main", "initialize privilege separation");
#ifdef ENABLE_PRIVSEP
	priv_init(PRIVSEP_CHROOT, ctl, uid, gid);
#else
	priv_init(PRIVSEP_CHROOT, ctl, 0, 0);
#endif

	/* Initialization of global configuration */
	if ((cfg = (struct lldpd *)
	    calloc(1, sizeof(struct lldpd))) == NULL)
		fatal("main", NULL);

	cfg->g_ctlname = ctlname;
	cfg->g_ctl = ctl;
	cfg->g_config.c_mgmt_pattern = mgmtp;
	cfg->g_config.c_cid_pattern = cidp;
	cfg->g_config.c_iface_pattern = interfaces;
	cfg->g_config.c_smart = smart;
	if (lldpcli)
		cfg->g_config.c_paused = 1;
	cfg->g_config.c_receiveonly = receiveonly;
	cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL;
	cfg->g_config.c_tx_hold = LLDPD_TX_HOLD;
	cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS;
#ifdef ENABLE_LLDPMED
	cfg->g_config.c_enable_fast_start = enable_fast_start;
	cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT;
	cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL;
#endif
#ifdef USE_SNMP
	cfg->g_snmp = snmp;
	cfg->g_snmp_agentx = agentx;
#endif /* USE_SNMP */
	cfg->g_config.c_bond_slave_src_mac_type = \
	    LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED;

	/* Get ioctl socket */
	log_debug("main", "get an ioctl socket");
	if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
		fatal("main", "failed to get ioctl socket");

	/* Description */
	if (!(cfg->g_config.c_advertise_version = advertise_version) &&
	    lsb_release && lsb_release[strlen(lsb_release) - 1] == '\n')
		lsb_release[strlen(lsb_release) - 1] = '\0';
	cfg->g_lsb_release = lsb_release;
        if (descr_override)
           cfg->g_config.c_description = descr_override;

	if (platform_override)
		cfg->g_config.c_platform = platform_override;

	/* Set system capabilities */
	log_debug("main", "set system capabilities");
	if ((lchassis = (struct lldpd_chassis*)
		calloc(1, sizeof(struct lldpd_chassis))) == NULL)
		fatal("localchassis", NULL);
	cfg->g_config.c_cap_advertise = 1;
	lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
	    LLDP_CAP_ROUTER | LLDP_CAP_STATION;
	cfg->g_config.c_mgmt_advertise = 1;
	TAILQ_INIT(&lchassis->c_mgmt);
#ifdef ENABLE_LLDPMED
	if (lldpmed > 0) {
		if (lldpmed == LLDP_MED_CLASS_III)
			lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
		lchassis->c_med_type = lldpmed;
		lchassis->c_med_cap_available = LLDP_MED_CAP_CAP |
		    LLDP_MED_CAP_IV | LLDP_MED_CAP_LOCATION |
		    LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD;
		cfg->g_config.c_noinventory = noinventory;
	} else
		cfg->g_config.c_noinventory = 1;
#endif

	/* Set TTL */
	lchassis->c_ttl = cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold;

	log_debug("main", "initialize protocols");
	cfg->g_protocols = protos;
	for (i=0; protos[i].mode != 0; i++) {

		/* With -ll, disable LLDP */
		if (protos[i].mode == LLDPD_MODE_LLDP)
			protos[i].enabled %= 3;
		/* With -ccc force CDPV2, enable CDPV1 */
		if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled == 3) {
			protos[i].enabled = 1;
		}
		/* With -cc force CDPV1, enable CDPV2 */
		if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 2) {
			protos[i].enabled = 1;
		}

		/* With -cccc disable CDPV1, enable CDPV2 */
		if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled >= 4) {
			protos[i].enabled = 0;
		}

		/* With -cccc disable CDPV1, enable CDPV2; -ccccc will force CDPv2 */
		if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 4) {
			protos[i].enabled = 1;
		}

		if (protos[i].enabled > 1)
			log_info("main", "protocol %s enabled and forced", protos[i].name);
		else if (protos[i].enabled)
			log_info("main", "protocol %s enabled", protos[i].name);
		else
			log_info("main", "protocol %s disabled", protos[i].name);
	    }

	TAILQ_INIT(&cfg->g_hardware);
	TAILQ_INIT(&cfg->g_chassis);
	TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
	lchassis->c_refcount++; /* We should always keep a reference to local chassis */

	/* Main loop */
	log_debug("main", "start main loop");
	levent_loop(cfg);
	lldpd_exit(cfg);
	free(cfg);

	return (0);
}