/*
 * fdstat (c) 2004 gophi
 *
 * 15:44:19  * gophi wkurwiony bo potrzebowal na szybko narzedzia 
 *             ktore wylistuje mu pidy procesow, ktore maja cos 
 *             pootwierane na konkretnej partycji, zapomnial nazwy 
 *             narzedzia (bo jest takie) i musial sam napisac na 
 *             szybko.
 *
 * listuje w postaci: pid <spacja> plik <spacja> linia komend (ze spacjami 
 * separujacymi argv), mozna przerzucic np. przez awk '{print $1}' zeby byl 
 * sam pid. dobre jesli chce sie na szybko naprawic konkretny filesystem 
 * a maszyna musi caly czas chodzic, wtedy killuje sie procesy, remount 
 * w trybie ro, odpala fsck, remount rw i odpala procesy. jesli proces 
 * ma pod dana sciezka wiecej niz jeden plik, to jest listowany tylko 
 * raz.
 *
 * skladnia: fdstat wzorzec, np. fdstat /var - wylistuje wszystko co ma 
 * /var w nazwie (nie tylko zaczyna sie na var).
 *
 * napisane na szybko.
 */

#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

void show_process (char *pname, char *lpath)
{
	char cmd[256];
	FILE *fp;
	int i, len;

	snprintf (cmd, sizeof(cmd), "/proc/%s/cmdline", pname);
	fp = fopen(cmd, "rb");
	if (!fp) {
		fprintf (stderr, "Otwieranie %s: %m.\n", cmd);
		return;
	}

	len = fread(cmd, 1, sizeof(cmd) - 1, fp);
	fclose (fp);

	if (!len) {
		fprintf (stderr, "Odczyt z /proc/%s/cmdline: %m.\n", pname);
		return;
	}

	for (i = 0; i < len; i++)
		if (!cmd[i])
			cmd[i] = 0x20;
	cmd[i] = (char) NULL;

	fprintf (stdout, "%s %s %s\n", pname, lpath, cmd);
}

char check_link (char *pname, char *name, char *pat)
{
	struct stat buf;
	char lname[96], lpath[256];
	int count;

	snprintf (lname, sizeof(lname), "/proc/%s/fd/%s", pname, name);
	if (lstat(lname, &buf)) {
		fprintf (stderr, "lstat %s: %m.\n", lname);
		return 0;
	}

	if (!S_ISLNK(buf.st_mode)) {
		fprintf (stderr, "%s nie jest symlinkiem.\n", lname);
		return 0;
	}

	count = readlink(lname, lpath, sizeof(lpath));
	if (count == -1) {
		fprintf (stderr, "Odczyt ścieżki linku %s: %m.\n", lname);
		return 0;
	}

	lpath[count] = (char) NULL;

	if (strstr(lpath, pat)) {
		show_process (pname, lpath);
		return 1;
	}

	return 0;
}

void check (char *name, char *pat)
{
	char dname[64];
	DIR *d;
	struct dirent *ent;

	snprintf (dname, sizeof(dname), "/proc/%s/fd", name);
	d = opendir(dname);
	if (!d) {
		fprintf (stderr, "Otwieranie %s: %m.\n", dname);
		return;
	}

	ent = readdir(d);
	while (ent) {
		if (ent->d_type & DT_LNK && check_link(name, ent->d_name, pat))
			break;
		ent = readdir(d);
	}

	closedir (d);
}

int main (int argc, char **argv)
{
	DIR *d;
	struct dirent *ent;

	if (argc < 2) {
		fprintf (stderr, "składnia: fdstat pattern\n");
		return -1;
	}

	d = opendir("/proc");
	if (!d) {
		fprintf (stderr, "Otwieranie /proc: %m.\n");
		return -1;
	}

	ent = readdir(d);
	while (ent) {
		if (ent->d_type & DT_DIR) {
			char *name = ent->d_name;

			while (*name)
				if (*name < '0' || *name > '9')
					break;
				else
					name++;

			if (!*name)
				check (ent->d_name, argv[1]);
		}
		ent = readdir(d);
	}
	closedir (d);

	return 0;
}
