Exemple #1
0
static int
options_apply(ProxyContext * const proxy_context)
{
    if (proxy_context->resolver_ip == NULL ||
        *proxy_context->resolver_ip == 0) {
        logger_noformat(proxy_context, LOG_ERR, "Resolver IP address required");
#ifdef _WIN32
        logger_noformat(proxy_context, LOG_ERR,
                        "Consult http://dnscrypt.org for details.");
#else
        logger_noformat(proxy_context, LOG_ERR,
                        "Please consult the dnscrypt-proxy(8) man page for details.");
#endif
        exit(1);
    }
    if (proxy_context->provider_name == NULL ||
        *proxy_context->provider_name == 0) {
        logger_noformat(proxy_context, LOG_ERR, "Provider name required");
        exit(1);
    }
    if (options_check_protocol_versions(proxy_context->provider_name) != 0) {
        logger_noformat(proxy_context, LOG_ERR,
                        "Unsupported server protocol version");
        exit(1);
    }
    if (proxy_context->provider_publickey_s == NULL) {
        logger_noformat(proxy_context, LOG_ERR, "Provider key required");
        exit(1);
    }
    if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s,
                                    proxy_context->provider_publickey) != 0) {
        logger_noformat(proxy_context, LOG_ERR, "Invalid provider key");
        exit(1);
    }
    if (proxy_context->daemonize) {
        do_daemonize();
    }
#ifndef _WIN32
    if (proxy_context->pid_file != NULL &&
        pid_file_create(proxy_context->pid_file,
                        proxy_context->user_id != (uid_t) 0) != 0) {
        logger_error(proxy_context, "Unable to create pid file");
    }
#endif
    if (proxy_context->log_file != NULL &&
        (proxy_context->log_fd = open(proxy_context->log_file,
                                      O_WRONLY | O_APPEND | O_CREAT,
                                      (mode_t) 0600)) == -1) {
        logger_error(proxy_context, "Unable to open log file");
        exit(1);
    }
    if (proxy_context->log_fd == -1 && proxy_context->daemonize) {
        logger_open_syslog(proxy_context);
    }
    return 0;
}
Exemple #2
0
static int
options_apply(ProxyContext * const proxy_context)
{
    if (proxy_context->resolver_ip == NULL) {
        options_usage();
        exit(1);
    }
    if (proxy_context->provider_name == NULL ||
        *proxy_context->provider_name == 0) {
        logger_noformat(proxy_context, LOG_ERR, "Provider name required");
        exit(1);
    }
    if (proxy_context->provider_publickey_s == NULL) {
        logger_noformat(proxy_context, LOG_ERR, "Provider key required");
        exit(1);
    }
    if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s,
                                    proxy_context->provider_publickey) != 0) {
        logger_noformat(proxy_context, LOG_ERR, "Invalid provider key");
        exit(1);
    }
    if (proxy_context->daemonize) {
        do_daemonize();
    }
#ifndef _WIN32
    if (proxy_context->pid_file != NULL &&
        pid_file_create(proxy_context->pid_file,
                        proxy_context->user_id != (uid_t) 0) != 0) {
        logger_error(proxy_context, "Unable to create pid file");
    }
#endif
    if (proxy_context->log_file != NULL &&
        (proxy_context->log_fd = open(proxy_context->log_file,
                                      O_WRONLY | O_APPEND | O_CREAT,
                                      (mode_t) 0600)) == -1) {
        logger_error(proxy_context, "Unable to open log file");
        exit(1);
    }
    if (proxy_context->log_fd == -1 && proxy_context->daemonize) {
        logger_open_syslog(proxy_context);
    }
    return 0;
}
Exemple #3
0
int run_client(int tunfd, const char *peer_addr_pair)
{
	struct timeval timeo;
	int sockfd, rc;
	fd_set rset;
	char s_peer_addr[44];

	if ((rc = v4pair_to_sockaddr(peer_addr_pair, ':', &peer_addr)) == 0) {
		/* DNS resolve OK, start service normally. */
		last_recv = time(NULL);
		inet_ntop(peer_addr.sin_family, &peer_addr.sin_addr,
			s_peer_addr, sizeof(s_peer_addr));
		printf("Mini virtual tunnelling client to %s:%u, interface: %s.\n",
			s_peer_addr, ntohs(peer_addr.sin_port), config.devname);
	} else if (rc == -EAGAIN && config.wait_dns) {
		/* Resolve later (last_recv = 0). */
		last_recv = 0;
		printf("Mini virtual tunnelling client, interface: %s. \n", config.devname);
		printf("WARNING: DNS resolution of '%s' temporarily unavailable, "
			"resolving later.\n", peer_addr_pair);
	} else if (rc == -EINVAL) {
		fprintf(stderr, "*** Invalid address pair '%s'.\n", peer_addr_pair);
		return -1;
	} else {
		fprintf(stderr, "*** Cannot resolve address pair '%s'.\n", peer_addr_pair);
		return -1;
	}


	/* The initial tunnelling connection. */
	if ((sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
		fprintf(stderr, "*** socket() failed: %s.\n", strerror(errno));
		exit(1);
	}
	set_nonblock(sockfd);

	/* Run in background. */
	if (config.in_background)
		do_daemonize();

	if (config.pid_file) {
		FILE *fp;
		if ((fp = fopen(config.pid_file, "w"))) {
			fprintf(fp, "%d\n", (int)getpid());
			fclose(fp);
		}
	}

	/* For triggering the first keep-alive packet to be sent. */
	last_keepalive = 0;

	for (;;) {
		FD_ZERO(&rset);
		FD_SET(tunfd, &rset);
		FD_SET(sockfd, &rset);

		timeo.tv_sec = 2;
		timeo.tv_usec = 0;

		rc = select((tunfd > sockfd ? tunfd : sockfd) + 1, &rset, NULL, NULL, &timeo);
		if (rc < 0) {
			fprintf(stderr, "*** select(): %s.\n", strerror(errno));
			return -1;
		}

		current_ts = time(NULL);
		if (last_recv > current_ts)
			last_recv = current_ts;
		if (last_keepalive > current_ts)
			last_keepalive = current_ts;

		/* Packet transmission timed out, send keep-alive packet. */
		if (current_ts - last_keepalive > config.keepalive_timeo) {
			peer_keepalive(sockfd);
		}

		/* Connection timed out, try reconnecting. */
		if (current_ts - last_recv > config.reconnect_timeo) {
			while (v4pair_to_sockaddr(peer_addr_pair, ':', &peer_addr) < 0) {
				fprintf(stderr, "Failed to resolve '%s', retrying.\n",
					peer_addr_pair);
				sleep(5);
			}

			/* Reconnected OK. Reopen the socket for a different local port. */
			close(sockfd);
			if ((sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
				fprintf(stderr, "*** socket() failed: %s.\n", strerror(errno));
				exit(1);
			}

			last_keepalive = 0;
			last_recv = current_ts;

			inet_ntop(peer_addr.sin_family, &peer_addr.sin_addr,
				s_peer_addr, sizeof(s_peer_addr));
			printf("Reconnected to %s:%u.\n", s_peer_addr, ntohs(peer_addr.sin_port));
			continue;
		}

		/* No result from select(), do nothing. */
		if (rc == 0)
			continue;

		if (FD_ISSET(sockfd, &rset)) {
			rc = network_receiving(tunfd, sockfd);
		}

		if (FD_ISSET(tunfd, &rset)) {
			rc = tunnel_receiving(tunfd, sockfd);
		}
	}

	return 0;
}
static int
options_apply(ProxyContext * const proxy_context)
{
    if (proxy_context->client_key_file != NULL) {
        if (proxy_context->ephemeral_keys != 0) {
            logger_noformat(proxy_context, LOG_ERR,
                            "--client-key and --ephemeral-keys are mutually exclusive");
            exit(1);
        }
        if (options_use_client_key_file(proxy_context) != 0) {
            logger(proxy_context, LOG_ERR,
                   "Client key file [%s] could not be used", proxy_context->client_key_file);
            exit(1);
        }
    }
    if (proxy_context->resolver_name != NULL) {
        if (proxy_context->resolvers_list == NULL) {
            logger_noformat(proxy_context, LOG_ERR,
                            "Resolvers list (-L command-line switch) required");
            exit(1);
        }
        if (options_use_resolver_name(proxy_context) != 0) {
            logger(proxy_context, LOG_ERR,
                   "Resolver name (-R command-line switch) required. "
                   "See [%s] for a list of public resolvers.",
                   proxy_context->resolvers_list);
            exit(1);
        }
    }
    if (proxy_context->resolver_ip == NULL ||
        *proxy_context->resolver_ip == 0 ||
        proxy_context->provider_name == NULL ||
        *proxy_context->provider_name == 0 ||
        proxy_context->provider_publickey_s == NULL ||
        *proxy_context->provider_publickey_s == 0) {
        logger_noformat(proxy_context, LOG_ERR,
                        "Resolver information required.");
        logger_noformat(proxy_context, LOG_ERR,
                        "The easiest way to do so is to provide a resolver name.");
        logger_noformat(proxy_context, LOG_ERR,
                        "Example: dnscrypt-proxy -R mydnsprovider");
        logger(proxy_context, LOG_ERR,
               "See the file [%s] for a list of compatible public resolvers",
               proxy_context->resolvers_list);
        logger_noformat(proxy_context, LOG_ERR,
                        "The name is the first column in this table.");
        logger_noformat(proxy_context, LOG_ERR,
                        "Alternatively, an IP address, a provider name "
                        "and a provider key can be supplied.");
#ifdef _WIN32
        logger_noformat(proxy_context, LOG_ERR,
                        "Consult https://dnscrypt.org "
                        "and https://github.com/jedisct1/dnscrypt-proxy/blob/master/README-WINDOWS.markdown "
                        "for details.");
#else
        logger_noformat(proxy_context, LOG_ERR,
                        "Please consult https://dnscrypt.org "
                        "and the dnscrypt-proxy(8) man page for details.");
#endif
        exit(1);
    }
    if (proxy_context->provider_name == NULL ||
        *proxy_context->provider_name == 0) {
        logger_noformat(proxy_context, LOG_ERR, "Provider name required");
        exit(1);
    }
    if (options_check_protocol_versions(proxy_context->provider_name) != 0) {
        logger_noformat(proxy_context, LOG_ERR,
                        "Unsupported server protocol version");
        exit(1);
    }
    if (proxy_context->provider_publickey_s == NULL) {
        logger_noformat(proxy_context, LOG_ERR, "Provider key required");
        exit(1);
    }
    if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s,
                                    proxy_context->provider_publickey) != 0) {
        logger_noformat(proxy_context, LOG_ERR, "Invalid provider key");
        exit(1);
    }
    if (proxy_context->daemonize != 0) {
        if (proxy_context->log_file == NULL) {
            proxy_context->syslog = 1;
        }
        do_daemonize();
    }
#ifndef _WIN32
    if (proxy_context->pid_file != NULL &&
        pid_file_create(proxy_context->pid_file,
                        proxy_context->user_id != (uid_t) 0) != 0) {
        logger_error(proxy_context, "Unable to create pid file");
        exit(1);
    }
#endif
    if (proxy_context->log_file != NULL && proxy_context->syslog != 0) {
        logger_noformat(proxy_context, LOG_ERR,
                        "--logfile and --syslog are mutually exclusive");
        exit(1);
    }
    if (proxy_context->log_file != NULL &&
        (proxy_context->log_fp = fopen(proxy_context->log_file, "a")) == NULL) {
        logger_error(proxy_context, "Unable to open log file");
        exit(1);
    }
    if (proxy_context->syslog != 0) {
        assert(proxy_context->log_fp == NULL);
        logger_open_syslog(proxy_context);
    }
    return 0;
}
Exemple #5
0
/**
 * Main entry point
 */
int
main(int argc, char **argv)
{
	TRACE_FUNC_START();
	int rc;

	/* accept command-line arguments */
	while ( (rc = getopt(argc, argv, "a:b:hsdf:")) != -1) {
		switch(rc) {
		case 'a':
			pc_info.rshell_args = (int8_t *)strdup(optarg);
			if (NULL == pc_info.rshell_args) {
				TRACE_ERR("Can't strdup remote shell args!!\n");
			}
			break;
		case 'b':
			pc_info.batch_size = atoi(optarg);
			TRACE_DEBUG_LOG("Taking batch_size as %d\n", 
					pc_info.batch_size);
			break;
		case 'd':
			pc_info.daemonize = 1;
			break;
		case 'h':
			print_help(*argv);
			break;
		case 'f':
			pc_info.lua_startup_file = (uint8_t *)strdup(optarg);
			if (NULL == pc_info.lua_startup_file) {
				TRACE_ERR("Can't strdup string for lua_startup_file: %s\n",
					  strerror(errno));
			}
			TRACE_DEBUG_LOG("Taking file %s as startup\n",
					pc_info.lua_startup_file);
			load_lua_file(optarg);
			break;
		case 's':
			pc_info.rshell = 1;
			break;
		default:
			print_help(*argv);
			break;
		}
	}

	/* init_modules */
	init_modules();

	/* check if rshell and daemonize are both enabled */
	if (pc_info.daemonize && pc_info.rshell) {
		fprintf(stdout, "You cannot create a daemon process and "
			"a remote shell at the same time!\n");
		fflush(stdout);
		print_help(*argv);
		return EXIT_FAILURE;
	}
	
	/* warp to remote shell, if asked */
	if (pc_info.rshell) {
		if (pc_info.rshell_args == NULL)
			fprintf(stdout, "Using localhost and port %d by default\n",
				BRICKS_LISTEN_PORT);
		lua_kickoff(LUA_EXE_REMOTE_SHELL, pc_info.rshell_args);
		return EXIT_SUCCESS;
	}

	/* daemonize, if asked */
	if ((pc_info.daemonize && (rc=do_daemonize()) == 0) ||
	    !pc_info.daemonize) {
		if (mark_pid_file(pv.pid_file) != 0) {
			TRACE_FUNC_END();
			TRACE_ERR("Can't lock the pid file.\n"
				  "Is a previous bricks daemon already running??\n");
		}
		export_global_socket();
		lua_kickoff((pc_info.daemonize) ? 
			    LUA_EXE_SCRIPT : LUA_EXE_HOME_SHELL, NULL);
	}

	/* initialize file printing mini-module */
	print_status_file();

	/* 
	 * if the user wants to daemonize and the process 
	 * is the child, then jump to accepting requests...
	 */
	if (pc_info.daemonize && rc == 0) {
		start_listening_reqs();
	}
	
	TRACE_FUNC_END();
	clean_exit(EXIT_SUCCESS);

	/* control will never come here */
	return EXIT_SUCCESS;
}
Exemple #6
0
int
main(int argc, char **argv)
{
	static struct option long_options[] =
	{
		{"config-file", required_argument, NULL, 'f'},
		{"verbose", no_argument, NULL, 'v'},
		{"monitoring-history", no_argument, NULL, 'm'},
		{"daemonize", no_argument, NULL, 'd'},
		{"pid-file", required_argument, NULL, 'p'},
		{NULL, 0, NULL, 0}
	};

	int			optindex;
	int			c,
				ret;
	bool		daemonize = false;
	FILE	   *fd;

	char		standby_version[MAXVERSIONSTR],
			   *ret_ver;

	progname = get_progname(argv[0]);

	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
		{
			help(progname);
			exit(SUCCESS);
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
			printf("%s %s (PostgreSQL %s)\n", progname, REPMGR_VERSION, PG_VERSION);
			exit(SUCCESS);
		}
	}

	while ((c = getopt_long(argc, argv, "f:v:mdp:", long_options, &optindex)) != -1)
	{
		switch (c)
		{
			case 'f':
				config_file = optarg;
				break;
			case 'v':
				verbose = true;
				break;
			case 'm':
				monitoring_history = true;
				break;
			case 'd':
				daemonize = true;
				break;
			case 'p':
				pid_file = optarg;
				break;
			default:
				usage();
				exit(ERR_BAD_CONFIG);
		}
	}

	if (daemonize)
	{
		do_daemonize();
	}

	if (pid_file)
	{
		check_and_create_pid_file(pid_file);
	}

#ifndef WIN32
	setup_event_handlers();
#endif

	/*
	 * Read the configuration file: repmgr.conf
	 */
	parse_config(config_file, &local_options);
	if (local_options.node == -1)
	{
		log_err(_("Node information is missing. "
				  "Check the configuration file, or provide one if you have not done so.\n"));
		terminate(ERR_BAD_CONFIG);
	}

	fd = freopen("/dev/null", "r", stdin);
	if (fd == NULL)
	{
		fprintf(stderr, "error reopening stdin to '/dev/null': %s",
				strerror(errno));
	}

	fd = freopen("/dev/null", "w", stdout);
	if (fd == NULL)
	{
		fprintf(stderr, "error reopening stdout to '/dev/null': %s",
				strerror(errno));
	}

	logger_init(&local_options, progname, local_options.loglevel,
				local_options.logfacility);
	if (verbose)
		logger_min_verbose(LOG_INFO);

	if (log_type == REPMGR_SYSLOG)
	{
		fd = freopen("/dev/null", "w", stderr);

		if (fd == NULL)
		{
			fprintf(stderr, "error reopening stderr to '/dev/null': %s",
					strerror(errno));
		}
	}

	xsnprintf(repmgr_schema, MAXLEN, "%s%s", DEFAULT_REPMGR_SCHEMA_PREFIX,
			 local_options.cluster_name);

	log_info(_("%s Connecting to database '%s'\n"), progname,
			 local_options.conninfo);
	my_local_conn = establish_db_connection(local_options.conninfo, true);

	/* should be v9 or better */
	log_info(_("%s Connected to database, checking its state\n"), progname);
	ret_ver = pg_version(my_local_conn, standby_version);
	if (ret_ver == NULL || strcmp(standby_version, "") == 0)
	{
		if (ret_ver != NULL)
			log_err(_("%s needs standby to be PostgreSQL 9.0 or better\n"),
					progname);
		terminate(ERR_BAD_CONFIG);
	}


	/*
	 * MAIN LOOP This loops cicles once per failover and at startup
	 * Requisites: - my_local_conn needs to be already setted with an active
	 * connection - no master connection
	 */
	do
	{
		/*
		 * Set my server mode, establish a connection to primary and start
		 * monitor
		 */
		ret = is_witness(my_local_conn, repmgr_schema,
						 local_options.cluster_name, local_options.node);

		if (ret == 1)
			my_local_mode = WITNESS_MODE;
		else if (ret == 0)
		{
			ret = is_standby(my_local_conn);

			if (ret == 1)
				my_local_mode = STANDBY_MODE;
			else if (ret == 0)	/* is the master */
				my_local_mode = PRIMARY_MODE;
		}

		/*
		 * XXX we did this before changing is_standby() to return int; we
		 * should not exit at this point, but for now we do until we have a
		 * better strategy
		 */
		if (ret == -1)
			terminate(1);

		switch (my_local_mode)
		{
			case PRIMARY_MODE:
				primary_options.node = local_options.node;
				strncpy(primary_options.conninfo, local_options.conninfo,
						MAXLEN);
				primary_conn = my_local_conn;

				check_cluster_configuration(my_local_conn);
				check_node_configuration();

				if (reload_config(config_file, &local_options))
				{
					PQfinish(my_local_conn);
					my_local_conn = establish_db_connection(local_options.conninfo, true);
					primary_conn = my_local_conn;
					update_registration();
				}

				log_info(_("%s Starting continuous primary connection check\n"),
						 progname);

				/*
				 * Check that primary is still alive, and standbies are
				 * sending info
				 */

				/*
				 * Every local_options.monitor_interval_secs seconds, do
				 * master checks XXX Check that standbies are sending info
				 */
				do
				{
					if (check_connection(primary_conn, "master"))
					{
						/*
						 * CheckActiveStandbiesConnections();
						 * CheckInactiveStandbies();
						 */
						sleep(local_options.monitor_interval_secs);
					}
					else
					{
						/*
						 * XXX May we do something more verbose ?
						 */
						terminate(1);
					}

					if (got_SIGHUP)
					{
						/*
						 * if we can reload, then could need to change
						 * my_local_conn
						 */
						if (reload_config(config_file, &local_options))
						{
							PQfinish(my_local_conn);
							my_local_conn = establish_db_connection(local_options.conninfo, true);
							primary_conn = my_local_conn;

							if (*local_options.logfile)
							{
								FILE	   *fd;

								fd = freopen(local_options.logfile, "a", stderr);
								if (fd == NULL)
								{
									fprintf(stderr, "error reopening stderr to '%s': %s",
									 local_options.logfile, strerror(errno));
								}

							}

							update_registration();
						}
						got_SIGHUP = false;
					}
				} while (!failover_done);
				break;

			case WITNESS_MODE:
			case STANDBY_MODE:
				/* I need the id of the primary as well as a connection to it */
				log_info(_("%s Connecting to primary for cluster '%s'\n"),
						 progname, local_options.cluster_name);
				primary_conn = get_master_connection(my_local_conn, repmgr_schema,
												  local_options.cluster_name,
												&primary_options.node, NULL);
				if (primary_conn == NULL)
				{
					terminate(ERR_BAD_CONFIG);
				}

				check_cluster_configuration(my_local_conn);
				check_node_configuration();

				if (reload_config(config_file, &local_options))
				{
					PQfinish(my_local_conn);
					my_local_conn = establish_db_connection(local_options.conninfo, true);
					update_registration();
				}

				/*
				 * Every local_options.monitor_interval_secs seconds, do
				 * checks
				 */
				if (my_local_mode == WITNESS_MODE)
				{
					log_info(_("%s Starting continuous witness node monitoring\n"),
							 progname);
				}
				else if (my_local_mode == STANDBY_MODE)
				{
					log_info(_("%s Starting continuous standby node monitoring\n"),
							 progname);
				}

				do
				{
					if (my_local_mode == WITNESS_MODE)
						witness_monitor();
					else if (my_local_mode == STANDBY_MODE)
						standby_monitor();
					sleep(local_options.monitor_interval_secs);

					if (got_SIGHUP)
					{
						/*
						 * if we can reload, then could need to change
						 * my_local_conn
						 */
						if (reload_config(config_file, &local_options))
						{
							PQfinish(my_local_conn);
							my_local_conn = establish_db_connection(local_options.conninfo, true);
							update_registration();
						}
						got_SIGHUP = false;
					}
				} while (!failover_done);
				break;
			default:
				log_err(_("%s: Unrecognized mode for node %d\n"), progname,
						local_options.node);
		}

		failover_done = false;

	} while (true);

	/* close the connection to the database and cleanup */
	close_connections();

	/* Shuts down logging system */
	logger_shutdown();

	return 0;
}
Exemple #7
0
int main(int argc, char *argv[])
{
    unsigned char tox_id[TOX_ADDRESS_SIZE];
    unsigned char tox_printable_id[TOX_ADDRESS_SIZE * 2 + 1];
    TOX_ERR_NEW tox_new_err;
    int oc;
    size_t save_size = 0;
    uint8_t *save_data = NULL;
    allowed_toxid *allowed_toxid_obj = NULL;

    log_init();

    while ((oc = getopt(argc, argv, "L:pi:C:s:P:dqhSF:DU:")) != -1)
    {
        switch(oc)
        {
            case 'L':
                /* Local port forwarding */
                client_mode = 1;
                client_local_port_mode = 1;
                if(parse_local_port_forward(optarg, &local_port, &remote_host, &remote_port) < 0)
                {
                    log_printf(L_ERROR, "Invalid value for -L option - use something like -L 22:127.0.0.1:22\n");
                    exit(1);
                }
                if(min_log_level == L_UNSET)
                {
                    min_log_level = L_INFO;
                }
                log_printf(L_DEBUG, "Forwarding remote port %d to local port %d\n", remote_port, local_port);
                break;
            case 'P':
                /* Pipe forwarding */
                client_mode = 1;
                client_pipe_mode = 1;
                if(parse_pipe_port_forward(optarg, &remote_host, &remote_port) < 0)
                {
                    log_printf(L_ERROR, "Invalid value for -P option - use something like -P 127.0.0.1:22\n");
                    exit(1);
                }
                if(min_log_level == L_UNSET)
                {
                    min_log_level = L_ERROR;
                }
                log_printf(L_INFO, "Forwarding remote port %d to stdin/out\n", remote_port);
                break;
            case 'p':
                /* Ping */
                client_mode = 1;
                ping_mode = 1;
                if(min_log_level == L_UNSET)
                {
                    min_log_level = L_INFO;
                }
                break;
            case 'i':
                /* Tox ID */
                server_whitelist_mode = 1;
                log_printf(L_DEBUG, "Server whitelist mode enabled");
                allowed_toxid_obj = (allowed_toxid *)calloc(sizeof(allowed_toxid), 1);
                if(!allowed_toxid_obj)
                {
                    log_printf(L_ERROR, "Could not allocate memory for allowed_toxid");
                    exit(1);
                }
                remote_tox_id = optarg;
                if(!string_to_id(allowed_toxid_obj->toxid, optarg))
                {
                    log_printf(L_ERROR, "Invalid Tox ID");
                    exit(1);
                }
                LL_APPEND(allowed_toxids, allowed_toxid_obj);
                break;
            case 'C':
                /* Config directory */
                strncpy(config_path, optarg, sizeof(config_path) - 1);
                if(optarg[strlen(optarg) - 1] != '/')
                {
                    int optarg_len = strlen(optarg);
                    
                    config_path[optarg_len] = '/';
                    config_path[optarg_len + 1] = '\0';
                }
                load_saved_toxid_in_client_mode = 1;
                break;
            case 's':
                /* Shared secret */
                use_shared_secret = 1;
                memset(shared_secret, 0, TOX_MAX_FRIEND_REQUEST_LENGTH);
                strncpy(shared_secret, optarg, TOX_MAX_FRIEND_REQUEST_LENGTH-1);
                break;
            case 'd':
                min_log_level = L_DEBUG;
                break;
            case 'q':
                min_log_level = L_ERROR;
                break;
            case 'S':
                use_syslog = 1;
                break;
            case 'D':
                daemonize = 1;
                use_syslog = 1;
                break;
            case 'F':
                pidfile = optarg;
                break;
            case 'U':
                daemon_username = optarg;
                break;
            case '?':
            case 'h':
            default:
                print_version();
                help();
                exit(1);
        }
    }

    if(!client_mode && min_log_level == L_UNSET)
    {
        min_log_level = L_INFO;
    }

    if(!client_mode && server_whitelist_mode)
    {
        log_printf(L_INFO, "Server in ToxID whitelisting mode - only clients listed with -i can connect");
    }

    if(daemonize)
    {
        do_daemonize();
    }

    atexit(cleanup);

    print_version();

    /* Bootstrap tox */
    tox_options_default(&tox_options);
    if((!client_mode) || load_saved_toxid_in_client_mode)
    {
        uint8_t *save_data = NULL;
        save_size = load_save(&save_data);
        if(save_data && save_size)
        {
            tox_options.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
            tox_options.savedata_data = save_data;
            tox_options.savedata_length = save_size;
        }
    }

    tox = tox_new(&tox_options, &tox_new_err);
    if(tox == NULL)
    {
        log_printf(L_DEBUG, "tox_new() failed (%u) - trying without proxy\n", tox_new_err);
        if((tox_options.proxy_type != TOX_PROXY_TYPE_NONE) || (tox_options.proxy_type = TOX_PROXY_TYPE_NONE, (tox = tox_new(&tox_options, &tox_new_err)) == NULL))
        {
            log_printf(L_DEBUG, "tox_new() failed (%u) - trying without IPv6\n", tox_new_err);
            if(!tox_options.ipv6_enabled || (tox_options.ipv6_enabled = 0, (tox = tox_new(&tox_options, &tox_new_err)) == NULL))
            {
                log_printf(L_DEBUG, "tox_new() failed (%u) - trying with Tor\n", tox_new_err);
                if((tox_options.proxy_type = TOX_PROXY_TYPE_SOCKS5, tox_options.proxy_host="127.0.0.1", tox_options.proxy_port=9050, (tox = tox_new(&tox_options, &tox_new_err)) == NULL))
                {
                    log_printf(L_ERROR, "tox_new() failed (%u) - exiting\n", tox_new_err);
                    exit(1);
                }
            }
        }
    }

    if(save_size && save_data)
    {
        free(save_data);
    }

    set_tox_username(tox);
    tox_callback_self_connection_status(tox, handle_connection_status_change, NULL);

    do_bootstrap(tox);

    if(client_mode)
    {
        tox_self_get_address(tox, tox_id);
        id_to_string(tox_printable_id, tox_id);
        tox_printable_id[TOX_ADDRESS_SIZE * 2] = '\0';
        log_printf(L_DEBUG, "Generated Tox ID: %s\n", tox_printable_id);

        if(!remote_tox_id)
        {
            log_printf(L_ERROR, "Tox id is required in client mode. Use -i 58435984ABCDEF475...\n");
            exit(1);
        }
        do_client_loop(remote_tox_id);
    }
    else
    {
        write_save(tox);

        if(!use_shared_secret)
        {
            log_printf(L_WARNING, "Shared secret authentication is not used - skilled attackers may connect to your tuntox server");
        }

        tox_self_get_address(tox, tox_id);
        memset(tox_printable_id, '\0', sizeof(tox_printable_id));
        id_to_string(tox_printable_id, tox_id);
        tox_printable_id[TOX_ADDRESS_SIZE * 2] = '\0';
        log_printf(L_INFO, "Using Tox ID: %s\n", tox_printable_id);

        tox_callback_friend_request(tox, accept_friend_request, NULL);
        do_server_loop();
    }

    return 0;
}
Exemple #8
0
int main(int argc, char *argv[])
{
	int c;
	int log_method = L_STDERR_SYSLOG;
	char *logfile = PATH_RADVD_LOG;
	int facility = LOG_FACILITY;
	char *username = NULL;
	char *chrootdir = NULL;
	int configtest = 0;
	int daemonize = 1;

	char const *pname = ((pname = strrchr(argv[0], '/')) != NULL) ? pname + 1 : argv[0];

	srand((unsigned int)time(NULL));

	char const *conf_path = PATH_RADVD_CONF;
	char const *daemon_pid_file_ident = PATH_RADVD_PID;

	/* parse args */
#define OPTIONS_STR "d:C:l:m:p:t:u:vhcn"
#ifdef HAVE_GETOPT_LONG
	int opt_idx;
	while ((c = getopt_long(argc, argv, OPTIONS_STR, prog_opt, &opt_idx)) > 0)
#else
	while ((c = getopt(argc, argv, OPTIONS_STR)) > 0)
#endif
	{
		switch (c) {
		case 'C':
			conf_path = optarg;
			break;
		case 'd':
			set_debuglevel(atoi(optarg));
			break;
		case 'f':
			facility = atoi(optarg);
			break;
		case 'l':
			logfile = optarg;
			break;
		case 'p':
			daemon_pid_file_ident = optarg;
			break;
		case 'm':
			if (!strcmp(optarg, "syslog")) {
				log_method = L_SYSLOG;
			} else if (!strcmp(optarg, "stderr_syslog")) {
				log_method = L_STDERR_SYSLOG;
			} else if (!strcmp(optarg, "stderr")) {
				log_method = L_STDERR;
			} else if (!strcmp(optarg, "logfile")) {
				log_method = L_LOGFILE;
			} else if (!strcmp(optarg, "none")) {
				log_method = L_NONE;
			} else {
				fprintf(stderr, "%s: unknown log method: %s\n", pname, optarg);
				exit(1);
			}
			break;
		case 't':
			chrootdir = strdup(optarg);
			break;
		case 'u':
			username = strdup(optarg);
			break;
		case 'v':
			version();
			break;
		case 'c':
			configtest = 1;
			break;
		case 'n':
			daemonize = 0;
			break;
		case 'h':
			usage(pname);
#ifdef HAVE_GETOPT_LONG
		case ':':
			fprintf(stderr, "%s: option %s: parameter expected\n", pname, prog_opt[opt_idx].name);
			exit(1);
#endif
		case '?':
			exit(1);
		}
	}

	/* TODO: Seems like this chroot'ing should happen *after* daemonizing for
	 * the sake of the PID file. */
	if (chrootdir) {
		if (!username) {
			fprintf(stderr, "Chroot as root is not safe, exiting\n");
			exit(1);
		}

		if (chroot(chrootdir) == -1) {
			perror("chroot");
			exit(1);
		}

		if (chdir("/") == -1) {
			perror("chdir");
			exit(1);
		}
		/* username will be switched later */
	}

	if (configtest) {
		set_debuglevel(1);
		log_method = L_STDERR;
	}

	if (log_open(log_method, pname, logfile, facility) < 0) {
		perror("log_open");
		exit(1);
	}

	if (!configtest) {
		flog(LOG_INFO, "version %s started", VERSION);
	}

	/* check that 'other' cannot write the file
	 * for non-root, also that self/own group can't either
	 */
	if (check_conffile_perm(username, conf_path) != 0) {
		if (get_debuglevel() == 0) {
			flog(LOG_ERR, "exiting, permissions on conf_file invalid");
			exit(1);
		} else
			flog(LOG_WARNING, "Insecure file permissions, but continuing anyway");
	}

	/* parse config file */
	struct Interface *ifaces = NULL;
	if ((ifaces = readin_config(conf_path)) == 0) {
		flog(LOG_ERR, "exiting, failed to read config file");
		exit(1);
	}

	if (configtest) {
		free_ifaces(ifaces);
		exit(0);
	}

	/* get a raw socket for sending and receiving ICMPv6 messages */
	int sock = open_icmpv6_socket();
	if (sock < 0) {
		perror("open_icmpv6_socket");
		exit(1);
	}

	/* if we know how to do it, check whether forwarding is enabled */
	if (check_ip6_forwarding()) {
		flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway");
	}

	int const pidfd = open_and_lock_pid_file(daemon_pid_file_ident);

	/*
	 * okay, config file is read in, socket and stuff is setup, so
	 * lets fork now...
	 */
	if (daemonize) {
		pid_t pid = do_daemonize(log_method, daemon_pid_file_ident);
		if (pid != 0 && pid != -1) {
			/* We want to see clean output from valgrind, so free username, chrootdir,
			 * and ifaces in the child process. */
			if (ifaces)
				free_ifaces(ifaces);

			if (username)
				free(username);

			if (chrootdir)
				free(chrootdir);

			exit(0);
		}
	} else {
		if (0 != write_pid_file(daemon_pid_file_ident, getpid())) {
			flog(LOG_ERR, "failure writing pid file detected");
			exit(-1);
		}
	}

	check_pid_file(daemon_pid_file_ident);

#ifdef __linux__
	/* for privsep */ {
		dlog(LOG_DEBUG, 3, "initializing privsep");

		int pipefds[2];

		if (pipe(pipefds) != 0) {
			flog(LOG_ERR, "Couldn't create privsep pipe.");
			return -1;
		}

		pid_t pid = fork();

		if (pid == -1) {
			flog(LOG_ERR, "Couldn't fork for privsep.");
			return -1;
		}

		if (pid == 0) {
			/* We want to see clean output from valgrind, so free username, chrootdir,
			 * and ifaces in the child process. */
			if (ifaces)
				free_ifaces(ifaces);

			if (username)
				free(username);

			if (chrootdir)
				free(chrootdir);

			close(pipefds[1]);

			privsep_init(pipefds[0]);
			_exit(0);
		}

		dlog(LOG_DEBUG, 3, "radvd privsep PID is %d", pid);

		/* Continue execution (will drop privileges soon) */
		close(pipefds[0]);
		privsep_set_write_fd(pipefds[1]);
	}
#endif

	if (username) {
		if (drop_root_privileges(username) < 0) {
			perror("drop_root_privileges");
			flog(LOG_ERR, "unable to drop root privileges");
			exit(1);
		}
		dlog(LOG_DEBUG, 3, "running as user: %s", username);
	}

	setup_ifaces(sock, ifaces);
	ifaces = main_loop(sock, ifaces, conf_path);
	stop_adverts(sock, ifaces);

	flog(LOG_INFO, "removing %s", daemon_pid_file_ident);
	unlink(daemon_pid_file_ident);
	close(pidfd);

	if (ifaces)
		free_ifaces(ifaces);

	if (chrootdir)
		free(chrootdir);

	if (username)
		free(username);

	flog(LOG_INFO, "returning from radvd main");
	log_close();

	return 0;
}
Exemple #9
0
int main(int argc, char **argv) {
	/* initial configuration, will be overridden by config file and command line options  */

	first_init	= 1;
	running		= 0;	// will be set to != 0 once honeytrap set up itself

	/* save command line arguments */
	arg_c = argc;
	arg_v = argv;


	/* the following are default values - change them in your configuration file */
	
	daemonize		= 1;		// default is to daemonize

#ifdef USE_PCAP_MON
	promisc_mode		= 0;		// no promisc mode
	pcap_offset		= 0;		// will be set after link type is determined
#endif

	log_level		= LOG_NOTICE;	// default log level
	logfile_fd		= STDOUT_FILENO;// default logfile, stdout will be replaced by logfile_fd
	
	u_id			= 0;		// root privileges per default
	g_id			= 0;

	conn_timeout		= 120;		// 2 minutes connect timeout
	read_timeout		= 1;		// 1 second read timeout
	m_read_timeout		= 60;		// 1 minute read timeout for mirror connections
	read_limit		= 0;		// 0 means no read limit
	
	conffile_name		= strdup("/etc/honeytrap/honeytrap.conf");
	pidfile_name		= strdup("/var/run/honeytrap.pid");
	logfile_name		= strdup("/var/log/honeytrap.log");
	response_dir		= strdup("/etc/honeytrap/responses");
	plugin_dir		= strdup("/etc/honeytrap/plugins");

#ifdef USE_PCAP_MON
	dev			= NULL;		// network device pointer
	packet_sniffer		= NULL;		// pcap device pointer
#endif

	portconf_default	= PORTCONF_NONE;

	eventlist		= NULL;		// list of timer-based events


	/* configure honeytrap */
	configure(arg_c, arg_v);

	
	/* daemonize (detach from console) */
	if (daemonize) do_daemonize();


	/* now initialize plugins */
	init_plugins();


	/* create pid file */
	create_pid_file();


	/* create IPC pipe and queue for port infos */
	if (pipe(portinfopipe) == -1) {
		logmsg(LOG_ERR, 0, "  Error - Unable to create port info IPC pipe: %m.\n");
		exit(EXIT_FAILURE);
	}
	if ((portinfoq = queue_new()) == NULL) {
		logmsg(LOG_ERR, 0, "  Error - Unable to create port info IPC pipe: %m.\n");
		exit(EXIT_FAILURE);
	}

	
	/* watch out for incoming connection requests */
	if (start_connection_monitor() < 0) clean_exit(EXIT_SUCCESS);
	
	return(0);
}
Exemple #10
0
int main(int argc, char *argv[]) {
    int rc, sync[2];

    pid_t pid = -1;

    siginfo_t status;

    struct mount *mounts = NULL;
    struct netif *netifs = NULL;
    struct cgroup *cgroups = NULL;
    struct user *users = NULL;
#if HAVE_LIBCAP_NG
    struct capability *caps = NULL;
#endif

    char *master;
    _close_ int master_fd = -1;

    char ephemeral_dir[] = "/tmp/pflask-ephemeral-XXXXXX";

    int clone_flags = CLONE_NEWNS  |
                          CLONE_NEWIPC |
                          CLONE_NEWPID |
#ifdef CLONE_NEWCGROUP
                          CLONE_NEWCGROUP |
#endif
                          CLONE_NEWUTS;

    struct gengetopt_args_info args;

    if (cmdline_parser(argc, argv, &args) != 0)
        return 1;

    for (unsigned int i = 0; i < args.mount_given; i++) {
        validate_optlist("--mount", args.mount_arg[i]);
        mount_add_from_spec(&mounts, args.mount_arg[i]);
    }

    for (unsigned int i = 0; i < args.netif_given; i++) {
        clone_flags |= CLONE_NEWNET;

        if (args.netif_arg != NULL) {
            validate_optlist("--netif", args.netif_arg[i]);
            netif_add_from_spec(&netifs, args.netif_arg[i]);
        }
    }

    if (args.user_given && !args.user_map_given) {
        uid_t uid;
        gid_t gid;

        clone_flags |= CLONE_NEWUSER;

        if (user_get_uid_gid(args.user_arg, &uid, &gid)) {
            user_add_map(&users, 'u', uid, uid, 1);
            user_add_map(&users, 'g', gid, gid, 1);
        }
    }

    for (unsigned int i = 0; i < args.user_map_given; i++) {
        size_t count;
        uid_t id, host_id;

        char *start = args.user_map_arg[i], *end = NULL;

        validate_optlist("--user-map", args.user_map_arg[i]);

        clone_flags |= CLONE_NEWUSER;

        id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        host_id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        count = strtoul(start, &end, 10);
        if (*end != '\0')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        user_add_map(&users, 'u', id, host_id, count);
        user_add_map(&users, 'g', id, host_id, count);
    }

    for (unsigned int i = 0; i < args.cgroup_given; i++)
        cgroup_add(&cgroups, args.cgroup_arg[i]);

#if HAVE_LIBCAP_NG
    for (unsigned int i = 0; i < args.caps_given; i++)
        capability_add(&caps, args.caps_arg[i]);
#endif

    if (args.no_userns_flag)
        clone_flags &= ~(CLONE_NEWUSER);

    if (args.no_mountns_flag)
        clone_flags &= ~(CLONE_NEWNS);

    if (args.no_netns_flag)
        clone_flags &= ~(CLONE_NEWNET);

    if (args.no_ipcns_flag)
        clone_flags &= ~(CLONE_NEWIPC);

    if (args.no_utsns_flag)
        clone_flags &= ~(CLONE_NEWUTS);

    if (args.no_pidns_flag)
        clone_flags &= ~(CLONE_NEWPID);

    if (args.attach_given) {
        master_fd = recv_pty(args.attach_arg);
        fail_if(master_fd < 0, "Invalid PID '%u'", args.attach_arg);

        process_pty(master_fd);
        return 0;
    }

    open_master_pty(&master_fd, &master);

    if (args.detach_flag)
        do_daemonize();

    sync_init(sync);

    if (args.ephemeral_flag) {
        if (!mkdtemp(ephemeral_dir))
            sysf_printf("mkdtemp()");
    }

    pid = do_clone(&clone_flags);

    if (!pid) {
        closep(&master_fd);

        rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
        sys_fail_if(rc < 0, "prctl(PR_SET_PDEATHSIG)");

        rc = setsid();
        sys_fail_if(rc < 0, "setsid()");

        sync_barrier_parent(sync, SYNC_START);

        sync_close(sync);

        open_slave_pty(master);

        setup_user(args.user_arg);

        if (args.hostname_given) {
            rc = sethostname(args.hostname_arg,
                             strlen(args.hostname_arg));
            sys_fail_if(rc < 0, "Error setting hostname");
        }

        setup_mount(mounts, args.chroot_arg, args.ephemeral_flag ?
                                               ephemeral_dir : NULL);

        if (args.chroot_given) {
            setup_nodes(args.chroot_arg);

            setup_ptmx(args.chroot_arg);

            setup_symlinks(args.chroot_arg);

            setup_console(args.chroot_arg, master);

            do_chroot(args.chroot_arg);
        }

        if (clone_flags & CLONE_NEWNET)
            config_netif();

        umask(0022);

#if HAVE_LIBCAP_NG
        setup_capabilities(caps);
#endif

        if (args.chdir_given) {
            rc = chdir(args.chdir_arg);
            sys_fail_if(rc < 0, "Error changing cwd");
        }

        if (args.chroot_given) {
            char *term = getenv("TERM");

            if (!args.keepenv_flag)
                clearenv();

            setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
            setenv("USER", args.user_arg, 1);
            setenv("LOGNAME", args.user_arg, 1);
            if (term)
                setenv("TERM", term, 1);
        }

        for (unsigned int i = 0; i < args.setenv_given; i++) {
            rc = putenv(strdup(args.setenv_arg[i]));
            sys_fail_if(rc != 0, "Error setting environment");
        }

        setenv("container", "pflask", 1);

        if (argc > optind)
            rc = execvpe(argv[optind], argv + optind, environ);
        else
            rc = execle("/bin/bash", "-bash", NULL, environ);

        sys_fail_if(rc < 0, "Error executing command");
    }

    sync_wait_child(sync, SYNC_START);

    if (args.chroot_given && (clone_flags & CLONE_NEWUSER))
        setup_console_owner(master, users);

    setup_cgroup(cgroups, pid);

    setup_netif(netifs, pid);

#ifdef HAVE_DBUS
    register_machine(pid, args.chroot_given ? args.chroot_arg : "");
#endif

    if (clone_flags & CLONE_NEWUSER)
        setup_user_map(users, pid);

    sync_wake_child(sync, SYNC_DONE);

    sync_close(sync);

    if (args.detach_flag)
        serve_pty(master_fd);
    else
        process_pty(master_fd);

    kill(pid, SIGKILL);

    rc = waitid(P_PID, pid, &status, WEXITED);
    sys_fail_if(rc < 0, "Error waiting for child");

    switch (status.si_code) {
    case CLD_EXITED:
        if (status.si_status != 0)
            err_printf("Child failed with code '%d'",
                       status.si_status);
        else
            ok_printf("Child exited");
        break;

    case CLD_KILLED:
        err_printf("Child was terminated by signal '%d'",
                   status.si_status);
        break;

    default:
        err_printf("Child failed");
        break;
    }

    sync_close(sync);

    clean_cgroup(cgroups);

    if (args.ephemeral_flag) {
        rc = rmdir(ephemeral_dir);
        sys_fail_if(rc != 0, "Error deleting ephemeral directory: %s",
                             ephemeral_dir);
    }

    cmdline_parser_free(&args);

    return status.si_status;
}
Exemple #11
0
int main(int argc, char *argv[]) {
	int rc, i;

	pid_t pid  = -1;
	pid_t ppid = getpid();

	uid_t uid = -1;
	gid_t gid = -1;

	_free_ char *user   = NULL;
	_free_ char *dest   = NULL;
	_free_ char *change = NULL;
	_free_ char *env    = NULL;
	_free_ char *cgroup = NULL;

	_close_ int master_fd = -1;

	char *master_name;

	int detach  = 0;
	int keepenv = 0;

	siginfo_t status;

	while ((rc = getopt_long(argc, argv, short_opts, long_opts, &i)) !=-1) {
		switch (rc) {
			case 'm':
				validate_optlist("--mount", optarg);

				add_mount_from_spec(optarg);
				break;

			case 'n':
				clone_flags |= CLONE_NEWNET;

				if (optarg != NULL) {
					validate_optlist("--netif", optarg);

					add_netif_from_spec(optarg);
				}
				break;

			case 'u':
				clone_flags |= CLONE_NEWUSER;

				freep(&user);

				user = strdup(optarg);
				break;

			case 'r':
				freep(&dest);

				dest = realpath(optarg, NULL);
				if (dest == NULL) sysf_printf("realpath()");
				break;

			case 'c':
				freep(&change);

				change = strdup(optarg);
				break;

			case 'g':
				validate_optlist("--cgroup", optarg);
				validate_cgroup_spec(optarg);

				freep(&change);

				cgroup = strdup(optarg);
				break;

			case 'd':
				detach = 1;
				break;

			case 'a': {
				char *end = NULL;
				pid = strtol(optarg, &end, 10);
				if (*end != '\0')
					fail_printf("Invalid value '%s' for --attach", optarg);
				break;
			}

			case 's': {
				validate_optlist("--setenv", optarg);

				if (env != NULL) {
					char *tmp = env;

					rc = asprintf(&env, "%s,%s", env, optarg);
					if (rc < 0) fail_printf("OOM");

					freep(&tmp);
				} else {
					env = strdup(optarg);
				}

				break;
			}

			case 'k':
				keepenv = 1;
				break;

			case 'U':
				clone_flags &= ~(CLONE_NEWUSER);
				break;

			case 'M':
				clone_flags &= ~(CLONE_NEWNS);
				break;

			case 'N':
				clone_flags &= ~(CLONE_NEWNET);
				break;

			case 'I':
				clone_flags &= ~(CLONE_NEWIPC);
				break;

			case 'H':
				clone_flags &= ~(CLONE_NEWUTS);
				break;

			case 'P':
				clone_flags &= ~(CLONE_NEWPID);
				break;

			case '?':
			case 'h':
				help();
				return 0;
		}
	}

	if (pid != -1) {
		master_fd = recv_pty(pid);
		if (master_fd < 0) fail_printf("Invalid PID '%u'", pid);

		pid = -1;
		goto process_fd;
	}

	if (user == NULL) {
		user = strdup("root");
		if (user == NULL) fail_printf("OOM");
	}

	open_master_pty(&master_fd, &master_name);

	uid = getuid();
	gid = getgid();

	if (detach == 1)
		do_daemonize();

	pid = do_clone();

	if (pid == 0) {
		closep(&master_fd);

		open_slave_pty(master_name);

		rc = setsid();
		if (rc < 0) sysf_printf("setsid()");

		rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
		if (rc < 0) sysf_printf("prctl(PR_SET_PDEATHSIG)");

		if (clone_flags & CLONE_NEWUSER)
			map_user_to_user(uid, gid, user);

		do_cgroup(cgroup, ppid);

		do_mount(dest);

		if (dest != NULL) {
			copy_nodes(dest);

			make_ptmx(dest);

			make_symlinks(dest);

			make_console(dest, master_name);

			do_chroot(dest);
		}

		if (clone_flags & CLONE_NEWNET)
			setup_loopback();

		umask(0022);

		/* TODO: drop capabilities */

		do_user(user);

		if (change != NULL) {
			rc = chdir(change);
			if (rc < 0) sysf_printf("chdir()");
		}

		if (dest != NULL) {
			char *term = getenv("TERM");

			if (keepenv == 0)
				clearenv();

			setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
			setenv("USER", user, 1);
			setenv("LOGNAME", user, 1);
			setenv("TERM", term, 1);
		}

		if (env != NULL) {
			size_t i, c;

			_free_ char **vars = NULL;

			_free_ char *tmp = strdup(env);
			if (tmp == NULL) fail_printf("OOM");

			c = split_str(tmp, &vars, ",");

			for (i = 0; i < c; i++) {
				rc = putenv(strdup(vars[i]));
				if (rc != 0) sysf_printf("putenv()");
			}
		}

		setenv("container", "pflask", 1);

		if (argc > optind)
			rc = execvpe(argv[optind], argv + optind, environ);
		else
			rc = execle("/bin/bash", "-bash", NULL, environ);

		if (rc < 0) sysf_printf("exec()");
	}

	do_netif(pid);

process_fd:
	if (detach == 1)
		serve_pty(master_fd);
	else
		process_pty(master_fd);

	if (pid == -1)
		return 0;

	kill(pid, SIGKILL);

	rc = waitid(P_PID, pid, &status, WEXITED);
	if (rc < 0) sysf_printf("waitid()");

	switch (status.si_code) {
		case CLD_EXITED:
			if (status.si_status != 0)
				err_printf("Child failed with code '%d'",
				           status.si_status);
			else
				ok_printf("Child exited");
			break;

		case CLD_KILLED:
			err_printf("Child was terminated");
			break;

		default:
			err_printf("Child failed");
			break;
	}

	undo_cgroup(cgroup, ppid);

	return status.si_status;
}