| /* vi: set sw=4 ts=4: */ | 
 | /* | 
 |  * tiny fuser implementation | 
 |  * | 
 |  * Copyright 2004 Tony J. White | 
 |  * | 
 |  * Licensed under GPLv2, see file LICENSE in this source tree. | 
 |  */ | 
 |  | 
 | //usage:#define fuser_trivial_usage | 
 | //usage:       "[OPTIONS] FILE or PORT/PROTO" | 
 | //usage:#define fuser_full_usage "\n\n" | 
 | //usage:       "Find processes which use FILEs or PORTs\n" | 
 | //usage:     "\n	-m	Find processes which use same fs as FILEs" | 
 | //usage:     "\n	-4,-6	Search only IPv4/IPv6 space" | 
 | //usage:     "\n	-s	Don't display PIDs" | 
 | //usage:     "\n	-k	Kill found processes" | 
 | //usage:     "\n	-SIGNAL	Signal to send (default: KILL)" | 
 |  | 
 | #include "libbb.h" | 
 |  | 
 | #define MAX_LINE 255 | 
 |  | 
 | #define OPTION_STRING "mks64" | 
 | enum { | 
 | 	OPT_MOUNT  = (1 << 0), | 
 | 	OPT_KILL   = (1 << 1), | 
 | 	OPT_SILENT = (1 << 2), | 
 | 	OPT_IP6    = (1 << 3), | 
 | 	OPT_IP4    = (1 << 4), | 
 | }; | 
 |  | 
 | typedef struct inode_list { | 
 | 	struct inode_list *next; | 
 | 	ino_t inode; | 
 | 	dev_t dev; | 
 | } inode_list; | 
 |  | 
 | struct globals { | 
 | 	int recursion_depth; | 
 | 	pid_t mypid; | 
 | 	inode_list *inode_list_head; | 
 | 	smallint kill_failed; | 
 | 	int killsig; | 
 | } FIX_ALIASING; | 
 | #define G (*(struct globals*)&bb_common_bufsiz1) | 
 | #define INIT_G() do { \ | 
 | 	G.mypid = getpid(); \ | 
 | 	G.killsig = SIGKILL; \ | 
 | } while (0) | 
 |  | 
 | static void add_inode(const struct stat *st) | 
 | { | 
 | 	inode_list **curr = &G.inode_list_head; | 
 |  | 
 | 	while (*curr) { | 
 | 		if ((*curr)->dev == st->st_dev | 
 | 		 && (*curr)->inode == st->st_ino | 
 | 		) { | 
 | 			return; | 
 | 		} | 
 | 		curr = &(*curr)->next; | 
 | 	} | 
 |  | 
 | 	*curr = xzalloc(sizeof(inode_list)); | 
 | 	(*curr)->dev = st->st_dev; | 
 | 	(*curr)->inode = st->st_ino; | 
 | } | 
 |  | 
 | static smallint search_dev_inode(const struct stat *st) | 
 | { | 
 | 	inode_list *ilist = G.inode_list_head; | 
 |  | 
 | 	while (ilist) { | 
 | 		if (ilist->dev == st->st_dev) { | 
 | 			if (option_mask32 & OPT_MOUNT) | 
 | 				return 1; | 
 | 			if (ilist->inode == st->st_ino) | 
 | 				return 1; | 
 | 		} | 
 | 		ilist = ilist->next; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | enum { | 
 | 	PROC_NET = 0, | 
 | 	PROC_DIR, | 
 | 	PROC_DIR_LINKS, | 
 | 	PROC_SUBDIR_LINKS, | 
 | }; | 
 |  | 
 | static smallint scan_proc_net_or_maps(const char *path, unsigned port) | 
 | { | 
 | 	FILE *f; | 
 | 	char line[MAX_LINE + 1], addr[68]; | 
 | 	int major, minor, r; | 
 | 	long long uint64_inode; | 
 | 	unsigned tmp_port; | 
 | 	smallint retval; | 
 | 	struct stat statbuf; | 
 | 	const char *fmt; | 
 | 	void *fag, *sag; | 
 |  | 
 | 	f = fopen_for_read(path); | 
 | 	if (!f) | 
 | 		return 0; | 
 |  | 
 | 	if (G.recursion_depth == PROC_NET) { | 
 | 		int fd; | 
 |  | 
 | 		/* find socket dev */ | 
 | 		statbuf.st_dev = 0; | 
 | 		fd = socket(AF_INET, SOCK_DGRAM, 0); | 
 | 		if (fd >= 0) { | 
 | 			fstat(fd, &statbuf); | 
 | 			close(fd); | 
 | 		} | 
 |  | 
 | 		fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x " | 
 | 			"%*x:%*x %*x:%*x %*x %*d %*d %llu"; | 
 | 		fag = addr; | 
 | 		sag = &tmp_port; | 
 | 	} else { | 
 | 		fmt = "%*s %*s %*s %x:%x %llu"; | 
 | 		fag = &major; | 
 | 		sag = &minor; | 
 | 	} | 
 |  | 
 | 	retval = 0; | 
 | 	while (fgets(line, MAX_LINE, f)) { | 
 | 		r = sscanf(line, fmt, fag, sag, &uint64_inode); | 
 | 		if (r != 3) | 
 | 			continue; | 
 |  | 
 | 		statbuf.st_ino = uint64_inode; | 
 | 		if (G.recursion_depth == PROC_NET) { | 
 | 			r = strlen(addr); | 
 | 			if (r == 8 && (option_mask32 & OPT_IP6)) | 
 | 				continue; | 
 | 			if (r > 8 && (option_mask32 & OPT_IP4)) | 
 | 				continue; | 
 | 			if (tmp_port == port) | 
 | 				add_inode(&statbuf); | 
 | 		} else { | 
 | 			if (major != 0 && minor != 0 && statbuf.st_ino != 0) { | 
 | 				statbuf.st_dev = makedev(major, minor); | 
 | 				retval = search_dev_inode(&statbuf); | 
 | 				if (retval) | 
 | 					break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	fclose(f); | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static smallint scan_recursive(const char *path) | 
 | { | 
 | 	DIR *d; | 
 | 	struct dirent *d_ent; | 
 | 	smallint stop_scan; | 
 | 	smallint retval; | 
 |  | 
 | 	d = opendir(path); | 
 | 	if (d == NULL) | 
 | 		return 0; | 
 |  | 
 | 	G.recursion_depth++; | 
 | 	retval = 0; | 
 | 	stop_scan = 0; | 
 | 	while (!stop_scan && (d_ent = readdir(d)) != NULL) { | 
 | 		struct stat statbuf; | 
 | 		pid_t pid; | 
 | 		char *subpath; | 
 |  | 
 | 		subpath = concat_subpath_file(path, d_ent->d_name); | 
 | 		if (subpath == NULL) | 
 | 			continue; /* . or .. */ | 
 |  | 
 | 		switch (G.recursion_depth) { | 
 | 		case PROC_DIR: | 
 | 			pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10); | 
 | 			if (errno != 0 | 
 | 			 || pid == G.mypid | 
 | 			/* "this PID doesn't use specified FILEs or PORT/PROTO": */ | 
 | 			 || scan_recursive(subpath) == 0 | 
 | 			) { | 
 | 				break; | 
 | 			} | 
 | 			if (option_mask32 & OPT_KILL) { | 
 | 				if (kill(pid, G.killsig) != 0) { | 
 | 					bb_perror_msg("kill pid %s", d_ent->d_name); | 
 | 					G.kill_failed = 1; | 
 | 				} | 
 | 			} | 
 | 			if (!(option_mask32 & OPT_SILENT)) | 
 | 				printf("%s ", d_ent->d_name); | 
 | 			retval = 1; | 
 | 			break; | 
 |  | 
 | 		case PROC_DIR_LINKS: | 
 | 			switch ( | 
 | 				index_in_substrings( | 
 | 					"cwd"  "\0" "exe"  "\0" | 
 | 					"root" "\0" "fd"   "\0" | 
 | 					"lib"  "\0" "mmap" "\0" | 
 | 					"maps" "\0", | 
 | 					d_ent->d_name | 
 | 				) | 
 | 			) { | 
 | 			enum { | 
 | 				CWD_LINK, | 
 | 				EXE_LINK, | 
 | 				ROOT_LINK, | 
 | 				FD_DIR_LINKS, | 
 | 				LIB_DIR_LINKS, | 
 | 				MMAP_DIR_LINKS, | 
 | 				MAPS, | 
 | 			}; | 
 | 			case CWD_LINK: | 
 | 			case EXE_LINK: | 
 | 			case ROOT_LINK: | 
 | 				goto scan_link; | 
 | 			case FD_DIR_LINKS: | 
 | 			case LIB_DIR_LINKS: | 
 | 			case MMAP_DIR_LINKS: | 
 | 				stop_scan = scan_recursive(subpath); | 
 | 				if (stop_scan) | 
 | 					retval = stop_scan; | 
 | 				break; | 
 | 			case MAPS: | 
 | 				stop_scan = scan_proc_net_or_maps(subpath, 0); | 
 | 				if (stop_scan) | 
 | 					retval = stop_scan; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 			break; | 
 | 		case PROC_SUBDIR_LINKS: | 
 |   scan_link: | 
 | 			if (stat(subpath, &statbuf) < 0) | 
 | 				break; | 
 | 			stop_scan = search_dev_inode(&statbuf); | 
 | 			if (stop_scan) | 
 | 				retval = stop_scan; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		free(subpath); | 
 | 	} | 
 | 	closedir(d); | 
 | 	G.recursion_depth--; | 
 | 	return retval; | 
 | } | 
 |  | 
 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 
 | int fuser_main(int argc UNUSED_PARAM, char **argv) | 
 | { | 
 | 	char **pp; | 
 |  | 
 | 	INIT_G(); | 
 |  | 
 | 	/* Handle -SIGNAL. Oh my... */ | 
 | 	pp = argv; | 
 | 	while (*++pp) { | 
 | 		int sig; | 
 | 		char *arg = *pp; | 
 |  | 
 | 		if (arg[0] != '-') | 
 | 			continue; | 
 | 		if (arg[1] == '-' && arg[2] == '\0') /* "--" */ | 
 | 			break; | 
 | 		if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') | 
 | 			continue; /* it's "-4" or "-6" */ | 
 | 		sig = get_signum(&arg[1]); | 
 | 		if (sig < 0) | 
 | 			continue; | 
 | 		/* "-SIGNAL" option found. Remove it and bail out */ | 
 | 		G.killsig = sig; | 
 | 		do { | 
 | 			pp[0] = arg = pp[1]; | 
 | 			pp++; | 
 | 		} while (arg); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	opt_complementary = "-1"; /* at least one param */ | 
 | 	getopt32(argv, OPTION_STRING); | 
 | 	argv += optind; | 
 |  | 
 | 	pp = argv; | 
 | 	while (*pp) { | 
 | 		/* parse net arg */ | 
 | 		unsigned port; | 
 | 		char path[sizeof("/proc/net/TCP6")]; | 
 |  | 
 | 		strcpy(path, "/proc/net/"); | 
 | 		if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2 | 
 | 		 && access(path, R_OK) == 0 | 
 | 		) { | 
 | 			/* PORT/PROTO */ | 
 | 			scan_proc_net_or_maps(path, port); | 
 | 		} else { | 
 | 			/* FILE */ | 
 | 			struct stat statbuf; | 
 | 			xstat(*pp, &statbuf); | 
 | 			add_inode(&statbuf); | 
 | 		} | 
 | 		pp++; | 
 | 	} | 
 |  | 
 | 	if (scan_recursive("/proc")) { | 
 | 		if (!(option_mask32 & OPT_SILENT)) | 
 | 			bb_putchar('\n'); | 
 | 		return G.kill_failed; | 
 | 	} | 
 |  | 
 | 	return EXIT_FAILURE; | 
 | } |