Example #1
0
void join(pid_t pid, int argc, char **argv, int index) {
	EUID_ASSERT();
	char *homedir = cfg.homedir;
	
	extract_command(argc, argv, index);
	signal (SIGTERM, signal_handler);

	// if the pid is that of a firejail  process, use the pid of the first child process
	EUID_ROOT();
	char *comm = pid_proc_comm(pid);
	EUID_USER();
	if (comm) {
		if (strcmp(comm, "firejail") == 0) {
			pid_t child;
			if (find_child(pid, &child) == 0) {
				pid = child;
				printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid);
			}
		}
		free(comm);
	}

	// check privileges for non-root users
	uid_t uid = getuid();
	if (uid != 0) {
		uid_t sandbox_uid = pid_get_uid(pid);
		if (uid != sandbox_uid) {
			fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n");
			exit(1);
		}
	}

	EUID_ROOT();
	// in user mode set caps seccomp, cpu, cgroup, etc
	if (getuid() != 0) {
		extract_caps_seccomp(pid);
		extract_cpu(pid);
		extract_cgroup(pid);
		extract_nogroups(pid);
		extract_user_namespace(pid);
	}
	
	// set cgroup
	if (cfg.cgroup)	// not available for uid 0
		set_cgroup(cfg.cgroup);
		
	// join namespaces
	if (arg_join_network) {
		if (join_namespace(pid, "net"))
			exit(1);
	}
	else if (arg_join_filesystem) {
		if (join_namespace(pid, "mnt"))
			exit(1);
	}
	else {
		if (join_namespace(pid, "ipc"))
			exit(1);
		if (join_namespace(pid, "net"))
			exit(1);
		if (join_namespace(pid, "pid"))
			exit(1);
		if (join_namespace(pid, "uts"))
			exit(1);
		if (join_namespace(pid, "mnt"))
			exit(1);
	}

	pid_t child = fork();
	if (child < 0)
		errExit("fork");
	if (child == 0) {
		// chroot into /proc/PID/root directory
		char *rootdir;
		if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
			errExit("asprintf");
			
		int rv;
		if (!arg_join_network) {
			rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option
			if (rv == 0)
				printf("changing root to %s\n", rootdir);
		}
		
		prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
		if (chdir("/") < 0)
			errExit("chdir");
		if (homedir) {
			struct stat s;
			if (stat(homedir, &s) == 0) {
				/* coverity[toctou] */
				if (chdir(homedir) < 0)
					errExit("chdir");
			}
		}
		
		// set cpu affinity
		if (cfg.cpus)	// not available for uid 0
			set_cpu_affinity();
					
		// set caps filter
		if (apply_caps == 1)	// not available for uid 0
			caps_set(caps);
#ifdef HAVE_SECCOMP
		// set protocol filter
		if (getuid() != 0)
			protocol_filter_load(RUN_PROTOCOL_CFG);
		if (cfg.protocol) {	// not available for uid 0
			protocol_filter();
		}
				
		// set seccomp filter
		if (apply_seccomp == 1)	// not available for uid 0
			seccomp_set();
#endif

		// fix qt 4.8
		if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
			errExit("setenv");
		if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
			errExit("setenv");

		// mount user namespace or drop privileges
		if (arg_noroot) {	// not available for uid 0
			if (arg_debug)
				printf("Joining user namespace\n");
			if (join_namespace(1, "user"))
				exit(1);

			// user namespace resets capabilities
			// set caps filter
			if (apply_caps == 1)	// not available for uid 0
				caps_set(caps);
		}
		else 
			drop_privs(arg_nogroups);	// nogroups not available for uid 0


		// set prompt color to green
		char *prompt = getenv("FIREJAIL_PROMPT");
		if (prompt && strcmp(prompt, "yes") == 0) {
			//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
			if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
				errExit("setenv");
		}
		
		// set nice
		if (arg_nice) {
			errno = 0;
			int rv = nice(cfg.nice);
			(void) rv;
			if (errno) {
				fprintf(stderr, "Warning: cannot set nice value\n");
				errno = 0;
			}
		}

		// run cmdline trough shell
		if (cfg.command_line == NULL) {
			// if the sandbox was started with --shell=none, it is possible we don't have a shell
			// inside the sandbox
			if (cfg.shell == NULL) {
				cfg.shell = guess_shell();
				if (!cfg.shell) {
					fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n");
					exit(1);
				}
			}
				
			struct stat s;
			if (stat(cfg.shell, &s) == -1)  {
				fprintf(stderr, "Error: %s shell not found inside the sandbox\n", cfg.shell);
				exit(1);
			}

			cfg.command_line = cfg.shell;
			cfg.window_title = cfg.shell;
		}

		int cwd = 0;
		if (cfg.cwd) {
			if (chdir(cfg.cwd) == 0)
				cwd = 1;
		}

		if (!cwd) {
			if (chdir("/") < 0)
				errExit("chdir");
			if (cfg.homedir) {
				struct stat s;
				if (stat(cfg.homedir, &s) == 0) {
					/* coverity[toctou] */
					if (chdir(cfg.homedir) < 0)
						errExit("chdir");
				}
			}
		}

		start_application();

		// it will never get here!!!
	}

	// wait for the child to finish
	waitpid(child, NULL, 0);
	flush_stdin();
	exit(0);
}
Example #2
0
void join(pid_t pid, int argc, char **argv, int index) {
	EUID_ASSERT();
	char *homedir = cfg.homedir;
	
	extract_command(argc, argv, index);

	// if the pid is that of a firejail  process, use the pid of the first child process
	EUID_ROOT();
	char *comm = pid_proc_comm(pid);
	EUID_USER();
	if (comm) {
		if (strcmp(comm, "firejail") == 0) {
			pid_t child;
			if (find_child(pid, &child) == 0) {
				pid = child;
				printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid);
			}
		}
		free(comm);
	}

	// check privileges for non-root users
	uid_t uid = getuid();
	if (uid != 0) {
		uid_t sandbox_uid = pid_get_uid(pid);
		if (uid != sandbox_uid) {
			fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n");
			exit(1);
		}
	}

	EUID_ROOT();
	// in user mode set caps seccomp, cpu, cgroup, etc
	if (getuid() != 0) {
		extract_caps_seccomp(pid);
		extract_cpu(pid);
		extract_cgroup(pid);
		extract_nogroups(pid);
		extract_user_namespace(pid);
	}
	
	// set cgroup
	if (cfg.cgroup)	// not available for uid 0
		set_cgroup(cfg.cgroup);
		
	// join namespaces
	if (arg_join_network) {
		if (join_namespace(pid, "net"))
			exit(1);
	}
	else if (arg_join_filesystem) {
		if (join_namespace(pid, "mnt"))
			exit(1);
	}
	else {
		if (join_namespace(pid, "ipc"))
			exit(1);
		if (join_namespace(pid, "net"))
			exit(1);
		if (join_namespace(pid, "pid"))
			exit(1);
		if (join_namespace(pid, "uts"))
			exit(1);
		if (join_namespace(pid, "mnt"))
			exit(1);
	}

	pid_t child = fork();
	if (child < 0)
		errExit("fork");
	if (child == 0) {
		// chroot into /proc/PID/root directory
		char *rootdir;
		if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
			errExit("asprintf");
			
		int rv;
		if (!arg_join_network) {
			rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option
			if (rv == 0)
				printf("changing root to %s\n", rootdir);
		}
		
		prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
		if (chdir("/") < 0)
			errExit("chdir");
		if (homedir) {
			struct stat s;
			if (stat(homedir, &s) == 0) {
				/* coverity[toctou] */
				if (chdir(homedir) < 0)
					errExit("chdir");
			}
		}
		
		// set cpu affinity
		if (cfg.cpus)	// not available for uid 0
			set_cpu_affinity();
					
		// set caps filter
		if (apply_caps == 1)	// not available for uid 0
			caps_set(caps);
#ifdef HAVE_SECCOMP
		// set protocol filter
		if (getuid() != 0)
			protocol_filter_load(RUN_PROTOCOL_CFG);
		if (cfg.protocol) {	// not available for uid 0
			protocol_filter();
		}
				
		// set seccomp filter
		if (apply_seccomp == 1)	// not available for uid 0
			seccomp_set();
		
#endif
		
		// fix qt 4.8
		if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
			errExit("setenv");
		if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
			errExit("setenv");

		// mount user namespace or drop privileges
		if (arg_noroot) {	// not available for uid 0
			if (arg_debug)
				printf("Joining user namespace\n");
			if (join_namespace(1, "user"))
				exit(1);
		}
		else 
			drop_privs(arg_nogroups);	// nogroups not available for uid 0

		// set prompt color to green
		//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
		if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
			errExit("setenv");

		// run cmdline trough /bin/bash
		if (cfg.command_line == NULL) {
			struct stat s;

			// replace the process with a shell
			if (stat("/bin/bash", &s) == 0)
				execlp("/bin/bash", "/bin/bash", NULL);
			else if (stat("/usr/bin/zsh", &s) == 0)
				execlp("/usr/bin/zsh", "/usr/bin/zsh", NULL);
			else if (stat("/bin/csh", &s) == 0)
				execlp("/bin/csh", "/bin/csh", NULL);
			else if (stat("/bin/sh", &s) == 0)
				execlp("/bin/sh", "/bin/sh", NULL);
			
			// no shell found, print an error and exit
			fprintf(stderr, "Error: no POSIX shell found\n");
			sleep(5);
			exit(1);
		}
		else {
			// run the command supplied by the user
			int cwd = 0;
			if (cfg.cwd) {
				if (chdir(cfg.cwd) == 0)
					cwd = 1;
			}
			
			if (!cwd) {
				if (chdir("/") < 0)
					errExit("chdir");
				if (cfg.homedir) {
					struct stat s;
					if (stat(cfg.homedir, &s) == 0) {
						if (chdir(cfg.homedir) < 0)
							errExit("chdir");
					}
				}
			}

			char *arg[5];
			arg[0] = "/bin/bash";
			arg[1] = "-c";
			if (arg_debug)
				printf("Starting %s\n", cfg.command_line);
			if (!arg_doubledash) {
				arg[2] = cfg.command_line;
				arg[3] = NULL;
			}
			else {
				arg[2] = "--";
				arg[3] = cfg.command_line;
				arg[4] = NULL;
			}
			execvp("/bin/bash", arg);
		}

		// it will never get here!!!
	}

	// wait for the child to finish
	waitpid(child, NULL, 0);
	exit(0);
}
Example #3
0
int sandbox(void* sandbox_arg) {
	// Get rid of unused parameter warning
	(void)sandbox_arg;

	pid_t child_pid = getpid();
	if (arg_debug)
		printf("Initializing child process\n");	

 	// close each end of the unused pipes
 	close(parent_to_child_fds[1]);
 	close(child_to_parent_fds[0]);
 
 	// wait for parent to do base setup
 	wait_for_other(parent_to_child_fds[0]);

	if (arg_debug && child_pid == 1)
		printf("PID namespace installed\n");

	//****************************
	// set hostname
	//****************************
	if (cfg.hostname) {
		if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0)
			errExit("sethostname");
	}

	//****************************
	// mount namespace
	//****************************
	// mount events are not forwarded between the host the sandbox
	if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
		chk_chroot();
	}
	
	
	//****************************
	// log sandbox data
	//****************************
	if (cfg.name)
		fs_logger2("sandbox name:", cfg.name);
	fs_logger2int("sandbox pid:", (int) sandbox_pid);
	if (cfg.chrootdir)
		fs_logger("sandbox filesystem: chroot");
	else if (arg_overlay)	
		fs_logger("sandbox filesystem: overlay");
	else
		fs_logger("sandbox filesystem: local");
	fs_logger("install mount namespace");
	
	//****************************
	// netfilter etc.
	//****************************
	if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter
		netfilter(arg_netfilter_file);
	}
	if (arg_netfilter6 && any_bridge_configured()) { // assuming by default the client filter
		netfilter6(arg_netfilter6_file);
	}

	// load IBUS env variables
	if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) {
		// do nothing - there are problems with ibus version 1.5.11
	}
	else
		env_ibus_load();
	
	// grab a copy of cp command
	fs_build_cp_command();
	
	// trace pre-install
	if (arg_trace || arg_tracelog)
		fs_trace_preload();

	//****************************
	// configure filesystem
	//****************************
#ifdef HAVE_SECCOMP
	int enforce_seccomp = 0;
#endif
#ifdef HAVE_CHROOT		
	if (cfg.chrootdir) {
		fs_chroot(cfg.chrootdir);
		// redo cp command
		fs_build_cp_command();
		
		// force caps and seccomp if not started as root
		if (getuid() != 0) {
			// force default seccomp inside the chroot, no keep or drop list
			// the list build on top of the default drop list is kept intact
			arg_seccomp = 1;
#ifdef HAVE_SECCOMP
			enforce_seccomp = 1;
#endif
			if (cfg.seccomp_list_drop) {
				free(cfg.seccomp_list_drop);
				cfg.seccomp_list_drop = NULL;
			}
			if (cfg.seccomp_list_keep) {
				free(cfg.seccomp_list_keep);
				cfg.seccomp_list_keep = NULL;
			}
			
			// disable all capabilities
			if (arg_caps_default_filter || arg_caps_list)
				fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n");
			arg_caps_drop_all = 1;
			
			// drop all supplementary groups; /etc/group file inside chroot
			// is controlled by a regular usr
			arg_nogroups = 1;
			if (!arg_quiet)
				printf("Dropping all Linux capabilities and enforcing default seccomp filter\n");
		}
		else
			arg_seccomp = 1;
						
		//****************************
		// trace pre-install, this time inside chroot
		//****************************
		if (arg_trace || arg_tracelog)
			fs_trace_preload();
	}
	else 
#endif		
	if (arg_overlay)	
		fs_overlayfs();
	else
		fs_basic_fs();
	

	//****************************
	// set hostname in /etc/hostname
	//****************************
	if (cfg.hostname) {
		fs_hostname(cfg.hostname);
	}
	
	//****************************
	// private mode
	//****************************
	if (arg_private) {
		if (cfg.home_private)	// --private=
			fs_private_homedir();
		else // --private
			fs_private();
	}
	
	if (arg_private_dev)
		fs_private_dev();
	if (arg_private_etc) {
		fs_private_etc_list();
		// create /etc/ld.so.preload file again
		if (arg_trace || arg_tracelog)
			fs_trace_preload();
	}
	if (arg_private_bin)
		fs_private_bin_list();
	if (arg_private_tmp)
		fs_private_tmp();
	
	//****************************
	// apply the profile file
	//****************************
	if (cfg.profile) {
		// apply all whitelist commands ... 
		fs_whitelist();
		
		// ... followed by blacklist commands
		fs_blacklist();
	}
	
	//****************************
	// install trace
	//****************************
	if (arg_trace || arg_tracelog)
		fs_trace();
		
	//****************************
	// update /proc, /dev, /boot directorymy
	//****************************
	fs_proc_sys_dev_boot();
	
	//****************************
	// --nosound and fix for pulseaudio 7.0
	//****************************
	if (arg_nosound)
		pulseaudio_disable();
	else
		pulseaudio_init();
	
	//****************************
	// networking
	//****************************
	if (arg_nonetwork) {
		net_if_up("lo");
		if (arg_debug)
			printf("Network namespace enabled, only loopback interface available\n");
	}
	else if (any_bridge_configured() || any_interface_configured()) {
		// configure lo and eth0...eth3
		net_if_up("lo");
		
		if (mac_not_zero(cfg.bridge0.macsandbox))
			net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox);
		sandbox_if_up(&cfg.bridge0);
		
		if (mac_not_zero(cfg.bridge1.macsandbox))
			net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox);
		sandbox_if_up(&cfg.bridge1);
		
		if (mac_not_zero(cfg.bridge2.macsandbox))
			net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox);
		sandbox_if_up(&cfg.bridge2);
		
		if (mac_not_zero(cfg.bridge3.macsandbox))
			net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox);
		sandbox_if_up(&cfg.bridge3);
		
		// add a default route
		if (cfg.defaultgw) {
			// set the default route
			if (net_add_route(0, 0, cfg.defaultgw))
				fprintf(stderr, "Warning: cannot configure default route\n");
		}
		
		// enable interfaces
		if (cfg.interface0.configured && cfg.interface0.ip) {
			if (arg_debug)
				printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev);
			net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu);
			net_if_up(cfg.interface0.dev);
		}			
		if (cfg.interface1.configured && cfg.interface1.ip) {
			if (arg_debug)
				printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev);
			net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu);
			net_if_up(cfg.interface1.dev);
		}			
		if (cfg.interface2.configured && cfg.interface2.ip) {
			if (arg_debug)
				printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev);
			net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu);
			net_if_up(cfg.interface2.dev);
		}			
		if (cfg.interface3.configured && cfg.interface3.ip) {
			if (arg_debug)
				printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev);
			net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu);
			net_if_up(cfg.interface3.dev);
		}			
			
		if (arg_debug)
			printf("Network namespace enabled\n");
	}
	
	// if any dns server is configured, it is time to set it now
	fs_resolvconf();
	fs_logger_print();
	fs_logger_change_owner();

	// print network configuration
	if (!arg_quiet) {
		if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) {
			printf("\n");
			if (any_bridge_configured() || any_interface_configured())
				net_ifprint();
			if (cfg.defaultgw != 0)
				printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw));
			if (cfg.dns1 != 0)
				printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1));
			if (cfg.dns2 != 0)
				printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2));
			if (cfg.dns3 != 0)
				printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3));
			printf("\n");
		}
	}
	
	fs_delete_cp_command();

	//****************************
	// set application environment
	//****************************
	prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
	int cwd = 0;
	if (cfg.cwd) {
		if (chdir(cfg.cwd) == 0)
			cwd = 1;
	}
	
	if (!cwd) {
		if (chdir("/") < 0)
			errExit("chdir");
		if (cfg.homedir) {
			struct stat s;
			if (stat(cfg.homedir, &s) == 0) {
				/* coverity[toctou] */
				if (chdir(cfg.homedir) < 0)
					errExit("chdir");
			}
		}
	}
	
	// set environment
	env_defaults();
	
	// set user-supplied environment variables
	env_apply();

	// set nice
	if (arg_nice) {
		errno = 0;
		int rv = nice(cfg.nice);
		(void) rv;
		if (errno) {
			fprintf(stderr, "Warning: cannot set nice value\n");
			errno = 0;
		}
	}
	
	// clean /tmp/.X11-unix sockets
	fs_x11();
	
	//****************************
	// set security filters
	//****************************
	// set capabilities
	if (!arg_noroot)
		set_caps();

	// set rlimits
	set_rlimits();

	// set seccomp
#ifdef HAVE_SECCOMP
	// install protocol filter
	if (cfg.protocol) {
		protocol_filter();	// install filter	
		protocol_filter_save();	// save filter in PROTOCOL_CFG
	}

	// if a keep list is available, disregard the drop list
	if (arg_seccomp == 1) {
		if (cfg.seccomp_list_keep)
			seccomp_filter_keep();
		else if (cfg.seccomp_list_errno)
			seccomp_filter_errno(); 
		else
			seccomp_filter_drop(enforce_seccomp);
	}
#endif

	// set cpu affinity
	if (cfg.cpus) {
		save_cpu(); // save cpu affinity mask to CPU_CFG file
		set_cpu_affinity();
	}
	
	// save cgroup in CGROUP_CFG file
	if (cfg.cgroup)
		save_cgroup();

	//****************************************
	// drop privileges or create a new user namespace
	//****************************************
	save_nogroups();
	if (arg_noroot) {
		int rv = unshare(CLONE_NEWUSER);
		if (rv == -1) {
			fprintf(stderr, "Error: cannot mount a new user namespace\n");
			perror("unshare");
			drop_privs(arg_nogroups);
		}
	}
	else
		drop_privs(arg_nogroups);
 	
	// notify parent that new user namespace has been created so a proper
 	// UID/GID map can be setup
 	notify_other(child_to_parent_fds[1]);
 	close(child_to_parent_fds[1]);
 
 	// wait for parent to finish setting up a proper UID/GID map
 	wait_for_other(parent_to_child_fds[0]);
 	close(parent_to_child_fds[0]);

	// somehow, the new user namespace resets capabilities;
	// we need to do them again
	if (arg_noroot) {
		set_caps();
		if (arg_debug)
			printf("noroot user namespace installed\n");
	}


	//****************************************
	// fork the application and monitor it
	//****************************************
	pid_t app_pid = fork();
	if (app_pid == -1)
		errExit("fork");
		
	if (app_pid == 0) {
		prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
		start_application();	// start app
	}

	monitor_application(app_pid);	// monitor application
	
	return 0;
}
Example #4
0
int main(int argc, char *argv[])
{ 
	configuration_t configuration;
	int i, j;
	struct timeval tm;
	struct timezone tz;
	measurement_t *measurement;
	struct timeval next, wait;
	int subject_id, flow_id;
	unsigned long long packets, bytes;
	double mbps;
	char command[MAX_COMMAND+1];
	char hostname_interface[MAX_HOSTNAME_INTERFACE+1];	/* DiMAPI connect string
															   as "hostname:interface,..." */

	struct timeval tv_start, tv_stop;	/* to measure how fast mapi_read_result()
														responds */
	int tv_diff_pkt, tv_diff_byte;		/* time used by mapi_read_results() */
	int tv_diff_threshold;		/* 1 if threshold was reached */
	mapi_results_t *pkt_counter_res;
	mapi_results_t *byte_counter_res;
	unsigned long long pkt_counter;
	unsigned long long byte_counter;
	int scope_size;
	double pkt_sec;	/* seconds from previous packet result */
	double byte_sec;	/* seconds from previous byte result */
   mapi_flow_info_t info;
   mapi_device_info_t dinfo;

	openlog("abw", LOG_PID, LOG_LOCAL0);
	syslog(LOG_DEBUG, "starting abw");

	memset((void *)&configuration, 0, (size_t)(sizeof(configuration)));

	/* Create global configuration */
	if ((configuration.global=malloc(sizeof(global_t)))==NULL) {
		fprintf(stderr, "%s: malloc() failed\n", __func__);
		return -1;
	}
	memset(configuration.global, 0, sizeof(global_t));

	/* Create first subject, scope, parameters and measurement so that they
      can be filled-in by command-line options */

	/* if ((configuration.subject=new_subject())==NULL) {
      fprintf(stderr, "%s: new_subject() failed\n", __func__);
      return -1;
	}
	if ((configuration.scope=new_scope())==NULL) {
      fprintf(stderr, "%s: new_subject() failed\n", __func__);
      return -1;
	}
	if ((configuration.parameters=new_parameters())==NULL) {
      fprintf(stderr, "%s: new_parameters() failed\n", __func__);
      return -1;
	}

	if ((configuration.measurement=new_measurement())==NULL) {
      fprintf(stderr, "%s: new_measurement() failed\n", __func__);
      return -1;
	} */

	/* Read command line */

	if (read_command_line(argc, argv, &configuration)<0) {
		fprintf(stderr, "%s: read_command_line() failed\n", __func__);
		return -1;
	}

	/* Read configuration file */

	if (configuration.global->conf_filename) {
		if (read_conf_file(&configuration)<0) {
			fprintf(stderr, "%s: read_conf_file() failed\n", __func__);
			return -1;
		}
	}

	/* Fill-in local hostname */

	if (get_local_hostname(&(configuration.global->hostname))<0) {
		fprintf(stderr, "%s: get_local_hostname() failed\n", __func__);
		return -1;
	}

	/* Check if specified values are within acceptable limits */

	if (check_conf(&configuration)<0) {
      fprintf(stderr, "%s: check_conf() failed\n", __func__);
      exit(-1);
	}

	/* Print configuration */

	if (debug)
		print_conf(&configuration);

	if (daemonize) {
		printf("Switching to daemon\n");
		if (continue_as_daemon()<0) {
			fprintf(stderr, "%s: continue_as_daemon() failed\n", __func__);
			return -1;
		}
		printf("Continuing as daemon\n");
	}

	/* 
	 * Create RRD files 
	 */

	/* Go over all measurements */

	measurement=configuration.measurement;
   while (measurement) {

		int parameters_id;
		char *filename;

		parameters_id = measurement->parameters_id;

		/* Go over all protocols */

		j=0;
		while (protocols[j].protocol) {
			if ((filename=
				abw_rrd_create_filename(measurement->scope, 
					parameters_id, protocols[j].protocol))==NULL) {
				fprintf(stderr, "%s: rrd_create_filename() failed\n", 
					__func__);
				return -1;
			}

			if (abw_rrd_create_file(filename)<0) {
				fprintf(stderr, "%s: abw_rrd_create_file() failed\n", __func__);
				return -1;
			}

			j++;
		} /* Go over all protocols */

		/* Go over all tracked protocols */

		j=0;
		while (tracked_protocols[j].protocol) {
			if ((filename=
				abw_rrd_create_filename(measurement->scope, 
					parameters_id, tracked_protocols[j].protocol))==NULL) {
				fprintf(stderr, "%s: rrd_create_filename() failed\n", 
					__func__);
				return -1;
			}

			if (abw_rrd_create_file(filename)<0) {
				fprintf(stderr, "%s: abw_rrd_create_file() failed\n", __func__);
				return -1;
			}

			j++;
		} /* Go over all tracked protocols */

		/* Create RRD file for "all" protocol (all traffic together) */

		if ((filename=
			abw_rrd_create_filename(measurement->scope, 
				parameters_id, "all"))==NULL) {
				fprintf(stderr, "%s: rrd_create_filename() failed\n", 
					__func__);
				return -1;
		}

		if (abw_rrd_create_file(filename)<0) {
			fprintf(stderr, "%s: abw_rrd_create_file() failed\n", __func__);
			return -1;
		}

		measurement=measurement->next;

   } /* while (measurement) */

	/* 
	 * Create MAPI flows 
	 */

	flow_id=0;

	/* Go over all measurements */

	measurement=configuration.measurement;
   while (measurement) {

		/* Go over all monitored protocols */

		i=0;
		while (measurement->protocols_array[i] && i<MAX_PROTOCOLS) {

			int parameters_id;
			char *protocol;

			/* Create data structure to maintain MAPI information */

			if (flow_id>=MAX_FLOWS) {
				fprintf(stderr, "%s: more than %d flows requested\n", __func__,
					MAX_FLOWS);
				return -1;
			}

			if ((flow[flow_id]=new_flow())==NULL) {
  				fprintf(stderr, "%s: new_flow() failed\n", __func__);
  				return -1;
  			}
			flow[flow_id]->measurement=measurement;
			flow[flow_id]->protocol=measurement->protocols_array[i];

			parameters_id = measurement->parameters_id;
			protocol = measurement->protocols_array[i];
				
			if ((flow[flow_id]->rrd_filename=
				abw_rrd_create_filename(measurement->scope, 
					parameters_id, protocol))==NULL) {
				fprintf(stderr, "%s: rrd_create_filename() failed\n", 
					__func__);
				return -1;
			}

			/* 
			 * If scope has only one subject and if hostname is "localhost" or
			 * equal to local hostname, then use MAPI connect string (not DiMAPI)
			 */

			if (!(measurement->scope->subject[1]) && 
				 (!strcmp(measurement->scope->subject[0]->hostname, "localhost") ||
				  !strcmp(measurement->scope->subject[0]->hostname, 
				  		configuration.global->hostname)))

				strcpy(hostname_interface, 
					measurement->scope->subject[0]->interface);

			/* 
			 * Prepare DiMAPI connect string as hostname:interface, ... 
			 */

			else {
				
				j=0; hostname_interface[0]='\0';
      		while (measurement->scope->subject[j] && j<MAX_SUBJECTS) {

					/* Append comma "," */

					if (hostname_interface[0]) {
						if (strlen(hostname_interface)+1>=MAX_HOSTNAME_INTERFACE) {
							fprintf(stderr, "%s: DiMAPI connect string is longer than %d characters\n", __func__, MAX_HOSTNAME_INTERFACE);
							return -1;
						}
						strcat(hostname_interface, ",");
					}

					/* Append next hostname:interface */
					if (strlen(hostname_interface) +
						 strlen(measurement->scope->subject[j]->hostname) +
				 		strlen(measurement->scope->subject[j]->interface) 
							>= MAX_HOSTNAME_INTERFACE) {
         			fprintf(stderr, "%s: DiMAPI connect string is longer than %d characters\n", __func__, MAX_HOSTNAME_INTERFACE);
            		return -1;
         		}
					sprintf(hostname_interface + strlen(hostname_interface), "%s:%s",
						measurement->scope->subject[j]->hostname,
						measurement->scope->subject[j]->interface);
			
					j++;
				} /* while (measurement->scope->subject[j] && j<MAX_SUBJECTS) */

			} /* Creating DiMAPI connect string */

			/* Create a new MAPI flow */

			if (debug)
				printf("%s: mapi_create_flow(%s)\n", __func__, hostname_interface);

  			if ((flow[flow_id]->fd=mapi_create_flow(hostname_interface))<0) {
				fprintf(stderr, "%s: mapi_create_flow(%s) failed\n", __func__,
					hostname_interface);
				fprintf(stderr, "%s: Do you run mapid daemon on the machine where you connect to?\n", __func__);
				fprintf(stderr, "%s: Do you run mapicommd daemon on the machine where you connect to? (if you are connecting to a non-local machine or to multiple machines)\n", __func__);
					return -1;
			}

         /* If this is a MAPI flow (not DiMAPI flow), then set MPLS and VLAN 
				flags according to mapi.conf. Otherwise the flags were set in
				abw.conf */

			if (!strchr(hostname_interface, ':')) {

				if (debug)
					printf("%s: MAPI flow on \"%s\", setting MPLS and VLAN flags from mapi.conf\n", __func__, hostname_interface);

         	if ((mapi_get_flow_info(flow[flow_id]->fd, &info)) < 0){
            	fprintf(stderr, "%s: mapi_get_flow_info() failed\n", __func__);
            	return -1;
         	}

         	if ((mapi_get_device_info(info.devid, &dinfo)) < 0) {
            	fprintf(stderr, "%s: mapi_get_device_info() failed\n", __func__);
            	return -1;
         	}

         	measurement->scope->mpls = dinfo.mpls;
         	measurement->scope->vlan = dinfo.vlan;

			}
			else
				if (debug)
               printf("%s: DiMAPI flow on \"%s\", setting MPLS and VLAN flags from abw.conf\n", __func__, hostname_interface);

			/* Prepare header filter for this protocol */

			if ((flow[flow_id]->tracked_protocol=
				protocol_filter(measurement->parameters->header_filter, 
					flow[flow_id]->protocol, measurement->scope->mpls, 
					measurement->scope->vlan,
					&(flow[flow_id]->header_filter)))<0) {
				fprintf(stderr, "%s: protocol_filter() failed\n", __func__);
				return -1;
			}

			if (debug)
				printf("measurement->parameters->header_filter: %s, flow[flow_id]->protocol: %s, flow[flow_id]->header_filter: %s, track_function: %s\n", (measurement->parameters->header_filter)?measurement->parameters->header_filter:"NULL", flow[flow_id]->protocol, (flow[flow_id]->header_filter)?flow[flow_id]->header_filter:"NULL", (flow[flow_id]->tracked_protocol)?tracked_protocols[flow[flow_id]->tracked_protocol-1].track_function:"none");

			/* Filter based on input port, we can use port number in the first 
				subject of the scope, because all subjects in a scope must have
				the same port number */

			if (measurement->scope->subject[0]->port >= 0) {
				if ((flow[flow_id]->interface_fid=mapi_apply_function(flow[flow_id]->fd, "INTERFACE", measurement->scope->subject[0]->port))<0) {
					fprintf(stderr, "%s: INTERFACE failed\n", __func__);
               return -1;
            }
			}

			/* Note that BPF_FILTER uses compiled header filter that
				selects packets of the given protocol */

			/* BPF_FILTER is applied if a) header_filter was specified in
				[parameters] section or b) protocol other than "all" and other than
				some that requires tracking was specified in [parameters] section or
				c) MPLS is used on links in this [scope] */

			if (flow[flow_id]->header_filter) {
				if (debug)
					printf("%s: mapi_apply_function(%d, BPF_FILTER, \"%s\")\n",
						__func__, flow[flow_id]->fd, flow[flow_id]->header_filter);
				if ((flow[flow_id]->bpf_filter_fid=
					mapi_apply_function(flow[flow_id]->fd, "BPF_FILTER", 
						flow[flow_id]->header_filter))<0) {
						fprintf(stderr, "%s: BPF_FILTER (\"%s\") failed\n", 
							__func__, flow[flow_id]->header_filter);
						return -1;
				}
			}

			/* Track application protocol, BPF_FILTER could have been applied 
				before */

			if (flow[flow_id]->tracked_protocol) {
				if (debug)
					printf("%s: mapi_apply_function(%d, %s)\n", __func__, 
						flow[flow_id]->fd,
						tracked_protocols[flow[flow_id]->tracked_protocol-1].
							track_function);
				if ((flow[flow_id]->track_function_fid=
					mapi_apply_function(flow[flow_id]->fd, 
						tracked_protocols[flow[flow_id]->tracked_protocol-1].
							track_function))<0) {
					fprintf(stderr, "%s: tracking (%s) failed\n", __func__, 
						tracked_protocols[flow[flow_id]->tracked_protocol-1].
							track_function);
					return -1;
				}
			}

			/* Sampling */

			if (measurement->parameters->sau_mode == 'd' && 
				 (unsigned int)(measurement->parameters->sau_threshold) != 1) {
				if ((flow[flow_id]->sample_fid=
					mapi_apply_function(flow[flow_id]->fd, "SAMPLE", 
						measurement->parameters->sau_threshold, PERIODIC))<0) {
					fprintf(stderr, "%s: SAMPLE (PERIODIC, %.02f) failed\n",
						__func__, measurement->parameters->sau_threshold);
					return -1;
				}
			}
			else if (measurement->parameters->sau_mode == 'p' && 
						(unsigned int)(measurement->parameters->sau_threshold) != 1) {
				if ((flow[flow_id]->sample_fid=
        			mapi_apply_function(flow[flow_id]->fd, "SAMPLE", 
						(measurement->parameters->sau_threshold)*100,
						PROBABILISTIC))<0) {
        			fprintf(stderr, "%s: SAMPLE (PROBABILISTIC, %.02f) failed\n", 
						__func__, (measurement->parameters->sau_threshold)*100);
        			return -1;
      		}
			}
	
			/* Payload searching */
	
			if (measurement->parameters->payload_strings[0]) {
				if ((flow[flow_id]->str_search_fid=
        			mapi_apply_function(flow[flow_id]->fd, "STR_SEARCH", 
					measurement->parameters->payload_strings[0], 0, 0))<0) {
           			fprintf(stderr, "%s: STR_SEARCH (%s) failed\n", 
						__func__, measurement->parameters->payload_strings[0]);
        	   		return -1;
       		}
			}

			/* Counting packets and bytes */

			if ((flow[flow_id]->pkt_counter_fid=
        		mapi_apply_function(flow[flow_id]->fd, "PKT_COUNTER"))<0) {
           		fprintf(stderr, "%s: PKT_COUNTER failed\n", __func__);
        		return -1;
  			}

			/* Simultaneous use of PKT_COUNTER and BYTE_COUNTER does not
				work with DAG4.3GE. Temporary hack: always use stflib version */

			if ((flow[flow_id]->byte_counter_fid=
        		mapi_apply_function(flow[flow_id]->fd, "stdflib:BYTE_COUNTER"))<0) {
           		fprintf(stderr, "%s: BYTE_COUNTER failed\n", 
				__func__);
        		return -1;
  			}

			/* Connect to flow */

			if (!configuration.global->no_measure) {
				if (mapi_connect(flow[flow_id]->fd)<0) {
					fprintf(stderr, "%s: mapi_connect() (%s) failed\n", 
						__func__, hostname_interface);
					return -1;
				}

				if ((scope_size=mapi_get_scope_size(flow[flow_id]->fd)) != 
					flow[flow_id]->measurement->scope->subject_no) {
					fprintf(stderr, "%s: mapi_get_scope_size() returned %d for %d subjects\n", __func__, scope_size, flow[flow_id]->measurement->scope->subject_no);
					return -1;
				}
			}

			i++;
			flow_id++;

		} /* while (measurement->protocols_array[i] && i<MAX_PROTOCOLS) */

		measurement=measurement->next;

	} /* while (measurement) */

	if (configuration.global->no_measure || !configuration.measurement)
		return 0;

	/* Periodically get results from MAPI flows */

	while (1) {
		if (gettimeofday(&tm, &tz)<0) {
			fprintf(stderr, "%s: gettimeofday() failed\n", __func__);
			return -1;
		}

		flow_id=0;
		while (flow[flow_id] && flow_id<MAX_FLOWS) {

			int scope_packets, scope_bytes;
	
			if (!configuration.global->no_stdout) {
				printf("%d %u.%u", flow[flow_id]->measurement->scope->id, 
					(unsigned int)(tm.tv_sec), 
					(unsigned int)(tm.tv_usec));
				if (!configuration.global->stdout_simple)
					printf(" %s\n", flow[flow_id]->protocol);
			}

			gettimeofday(&tv_start, NULL);
			if ((pkt_counter_res=mapi_read_results(flow[flow_id]->fd, 
				flow[flow_id]->pkt_counter_fid))==NULL) {
					fprintf(stderr, "%s: mapi_read_results() for flow %d failed\n",
						__func__, flow_id);
			 	return -1;
			}

			gettimeofday(&tv_stop, NULL);
			tv_diff_pkt=timestamp_diff(&tv_start, &tv_stop);

			gettimeofday(&tv_start, NULL);
			if ((byte_counter_res=mapi_read_results(flow[flow_id]->fd, 
				flow[flow_id]->byte_counter_fid))==NULL) {
        			fprintf(stderr, "%s: mapi_read_results() for flow %d failed\n",
             		__func__, flow_id);
          		return -1;
     		}
			gettimeofday(&tv_stop, NULL);
			tv_diff_byte=timestamp_diff(&tv_start, &tv_stop);

			if (tv_diff_pkt>=TV_DIFF_THRESHOLD ||
				 tv_diff_byte>=TV_DIFF_THRESHOLD)
				tv_diff_threshold=1;
			else
				tv_diff_threshold=0;

			if (tv_diff_pkt>=TV_DIFF_THRESHOLD)
				syslog(LOG_DEBUG, "mapi_read_result() for PKT_COUNTER takes %d us for measurement ID %d and protocol %s (threshold %d us reached)", tv_diff_pkt, flow[flow_id]->measurement->id, flow[flow_id]->protocol, TV_DIFF_THRESHOLD);
			if (tv_diff_byte>=TV_DIFF_THRESHOLD)
				syslog(LOG_DEBUG, "mapi_read_result() for BYTE_COUNTER takes %d us for measurement ID %d and protocol %s (threshold %d us reached)", tv_diff_byte, flow[flow_id]->measurement->id, flow[flow_id]->protocol, TV_DIFF_THRESHOLD);

			scope_size = flow[flow_id]->measurement->scope->subject_no;

			scope_packets=0;
			scope_bytes=0;

			for (subject_id=0; subject_id<scope_size; subject_id++) {
	
				pkt_counter=
					*((unsigned long long*)(pkt_counter_res[subject_id].res));
				byte_counter=
					*((unsigned long long*)(byte_counter_res[subject_id].res));

				packets=pkt_counter - flow[flow_id]->pkt_counter[subject_id];
				bytes=byte_counter - flow[flow_id]->byte_counter[subject_id];
				mbps=(double)bytes*8/1000000;

				flow[flow_id]->pkt_counter[subject_id]=pkt_counter;
				flow[flow_id]->byte_counter[subject_id]=byte_counter;

				/* Determine seconds from previous result */

				if (flow[flow_id]->pkt_ts[subject_id])
					pkt_sec=(double)(pkt_counter_res[subject_id].ts -
								flow[flow_id]->pkt_ts[subject_id])/1000000;
				else
					pkt_sec=
						flow[flow_id]->measurement->parameters->interval.tv_sec +
	               (double)(flow[flow_id]->measurement->parameters->interval.tv_usec)/1000000;
		
				if (flow[flow_id]->byte_ts[subject_id])
					byte_sec=(double)(byte_counter_res[subject_id].ts -
						flow[flow_id]->byte_ts[subject_id])/1000000;
				else
					byte_sec=
						flow[flow_id]->measurement->parameters->interval.tv_sec +
	               (double)(flow[flow_id]->measurement->parameters->interval.tv_usec)/1000000;

				scope_packets+=(packets/pkt_sec);
				scope_bytes+=(bytes/byte_sec);

				flow[flow_id]->pkt_ts[subject_id]=
					pkt_counter_res[subject_id].ts;
				flow[flow_id]->byte_ts[subject_id]=
					byte_counter_res[subject_id].ts;

				if (tv_diff_threshold) {
					syslog(LOG_DEBUG, "%s:%s: %.02f seconds from previous result",
						flow[flow_id]->measurement->scope->subject[subject_id]->hostname,
						flow[flow_id]->measurement->scope->subject[subject_id]->interface,
						byte_sec);
				}

				/* Print result */

				if (!configuration.global->no_stdout) {
					if (configuration.global->stdout_simple)
						printf(" %0.2f %0.2f %0.2f", packets/pkt_sec, 
							bytes/byte_sec, mbps/byte_sec);
					else
							printf(" %0.2f packets/s, %0.2f bytes/s, %0.2f Mb/s, time %uus/%uus, interval %0.2fs/%0.2fs\n", 
								packets/pkt_sec, bytes/byte_sec, mbps/byte_sec, 
								tv_diff_pkt, tv_diff_byte, pkt_sec, byte_sec);
				}

			} /* for (subject_id=0; subject_id++; subject_id<scope_size) */

			if (!configuration.global->no_stdout)
				printf("\n");

			/* If interval is at least 1 second, then store results 
			   to RRD file */
				
			if (flow[flow_id]->measurement->parameters->interval.tv_sec) {
				sprintf(command, "rrdtool update %s %u:%lu:%lu:%.6f", 
					 flow[flow_id]->rrd_filename, 
					 (unsigned int)(tm.tv_sec), 
					 (unsigned long)(scope_packets), 
					 (unsigned long)(scope_bytes), 
					 (double)scope_bytes*8/1000000);

				if (configuration.global->debug > 1)
					syslog(LOG_DEBUG, "system(%s)", command);

				if (tm.tv_sec == flow[flow_id]->rrd_ts)
					syslog(LOG_ERR, "duplicate RRD timestamp %u for scope %d\n", (unsigned int)(tm.tv_sec), flow[flow_id]->measurement->scope->id);
				else
					flow[flow_id]->rrd_ts=tm.tv_sec;

				if (debug)
					printf("%s: system(%s)\n", __func__, command);

				if (system(command)<0) {
					fprintf(stderr, "%s: command(%s) failed\n", __func__,
						command);
					return -1;
				}
			}

			flow_id++;

		} /* while (flow[flow_id] && flow_id<MAX_FLOWS) */

		abw_next_timestamp(&(configuration.measurement->parameters->interval), 
			&next, &wait);

		if (!configuration.global->no_stdout && 
			 !configuration.global->stdout_simple) {
     		printf("next.tv_sec: %d, next.tv_usec: %d, wait.tv_sec: %d, wait.tv_usec: %d\n", (int)(next.tv_sec), (int)(next.tv_usec), (int)(wait.tv_sec), (int)(wait.tv_usec));
			printf("===============================================================================\n");
		}

     	usleep(wait.tv_sec * 1000000 + wait.tv_usec);
		 
	} /* while (1) */

	return 0;
} /* main() */