Skip to content
Snippets Groups Projects
checkstat.c 5.93 KiB
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>

void
usage (char *argv0, int help)
{
	char *progname = strrchr(argv0, '/');

	if (progname == NULL)
		progname = argv0;

	fprintf (help ? stdout : stderr,
		 "Usage: %s [flags] file[s]\n",
		 progname);

	if (!help)
	{
		fprintf (stderr, "   or try '-h' for help\n");
		exit (1);
	}

	printf ("Check given files have...\n");
	printf (" -p    permission       file must have required permissions\n");
	printf (" -t    dir|file|link    file must be of the specified type\n");
	printf (" -l    link_name        file must be a link to the given name\n");
	printf (" -s    size             file must have the given size\n");
	printf (" -u    user             file must be owned by given user\n");
	printf (" -g    group            file must be owned by given group\n");
	printf (" -f                     follow symlinks\n");
	printf (" -a                     file must be absent\n");
	printf (" -v                     increase verbosity\n");
	printf (" -h                     print help\n");
	printf (" Exit status is 0 on success, 1 on failure\n");
}

int
main (int argc, char **argv)
{
	int           c;
	struct stat64 buf;
	int           perms = -1;
	uid_t         uid = (uid_t)-1;
	gid_t         gid = (gid_t)-1;
	char         *type = NULL;
	long          absent = 0;
	char         *checklink = NULL;
	int           verbose = 0;
	long long     size = -1;
	int           follow = 0;
	char         *term;

	while ((c = getopt (argc, argv, "p:t:l:s:u:g:avfh")) != -1)
		switch (c)
		{
		case 'p':
			perms = (int)strtol (optarg, &term, 0);
			if (term == optarg)
			{
				fprintf (stderr, "Can't parse permission %s\n", optarg);
				return (1);
			}
			break;
		case 'l':
			checklink = optarg;
			break;

		case 's':
			size = strtoll (optarg, &term, 0);
			if (term == optarg)
			{
				fprintf (stderr, "Can't parse size %s\n", optarg);
				return (1);
			}
			break;

		case 'u':
			if (*optarg == '#')
			{
				uid = (uid_t)strtol (optarg + 1, &term, 0);
				if (term == optarg + 1)
				{
					fprintf (stderr, "Can't parse numeric uid %s\n", optarg);
					return (1);
				}
			} else {
				struct passwd *pw = getpwnam (optarg);

				if (pw == NULL)
				{
					fprintf (stderr, "Can't find user %s\n", optarg);
					return (1);
				}
				uid = pw->pw_uid;
			}
			break;

		case 'g':
			if (*optarg == '#')
			{
				gid = (gid_t)strtol (optarg + 1, &term, 0);
				if (term == optarg + 1)
				{
					fprintf (stderr, "Can't parse numeric gid %s\n", optarg);
					return (1);
				}
			} else {
				struct group *gr = getgrnam (optarg);

				if (gr == NULL)
				{
					fprintf (stderr, "Can't find group %s\n", optarg);
					return (1);
				}
				uid = gr->gr_gid;
			}
			break;

		case 't':
			type = optarg;
			break;

		case 'a':
			absent = 1;
			break;

		case 'v':
			verbose++;
			break;

		case 'f':
			follow++;
			break;

		case 'h':
			usage (argv[0], 1);
			return (0);

		default:
			usage (argv[0], 0);
		}

	if (optind == argc)
		usage (argv[0], 0);

	do
	{
		char *fname = argv[optind];
		int rc = follow ? stat64 (fname, &buf) : lstat64 (fname, &buf);

		if (rc != 0)
		{
			if (!(absent && errno == ENOENT))
			{
				if (verbose)
					printf ("Can't %sstat %s: %s\n",
						follow ? "" : "l",
						fname, strerror (errno));
				return (1);
			}

			if (verbose)
				printf ("%s: absent OK\n", fname);
			continue;
		}

		if (absent)
		{
			if (verbose)
				printf ("%s exists\n", fname);
			return (1);
		}

		if (type != NULL)
		{
			if (!strcmp (type, "d") ||
			    !strcmp (type, "dir"))
			{
				if (!S_ISDIR (buf.st_mode))
				{
					if (verbose)
						printf ("%s is not a directory\n",
							 fname);
					return (1);
				}
			}
			else if (!strcmp (type, "f") ||
				 !strcmp (type, "file"))
			{
				if (!S_ISREG (buf.st_mode))
				{
					if (verbose)
						printf ("%s is not a regular file\n",
							fname);
					return (1);
				}
			}
			else if (!strcmp (type, "l") ||
				 !strcmp (type, "link"))
			{
				if (!S_ISLNK (buf.st_mode))
				{
					if (verbose)
						printf ("%s is not a link\n",
							fname);
					return (1);
				}
			}
			else
			{
				fprintf (stderr, "Can't parse file type %s\n",
					 type);
				return (1);
			}

			if (verbose)
				printf ("%s has type %s OK\n", fname, type);
		}

		if (perms != -1)
		{
			if ((buf.st_mode & ~S_IFMT) != perms)
			{
				if (verbose)
					printf ("%s has perms 0%o, not 0%o\n",
						fname, (buf.st_mode & ~S_IFMT),
						perms);
				return (1);
			}

			if (verbose)
				printf ("%s has perms 0%o OK\n",
					fname, perms);
		}

		if (size != -1)
		{
			if (buf.st_size != size)
			{
				if (verbose)
					printf ("%s has size %Ld, not %Ld\n",
						fname, (long long)buf.st_size,
						size);
				return (1);
			}

			if (verbose)
				printf ("%s has size %Ld OK\n", fname, size);
		}

		if (checklink != NULL)
		{
			static char lname[4<<10];

			rc = readlink (fname, lname, sizeof (lname) - 1);

			if (rc < 0)
			{
				if (verbose)
					printf ("%s: can't read link: %s\n",
						fname, strerror (errno));
				return (1);
			}

			lname[rc] = 0;
			if (strcmp (checklink, lname))
			{
				if (verbose)
					printf ("%s is a link to %s and not %s\n",
						fname, lname, checklink);
				return (1);
			}
			if (verbose)
				printf ("%s links to %s OK\n", fname, checklink);
		}

		if (uid != (uid_t)-1)
		{
			if (buf.st_uid != uid)
			{
				if (verbose)
					printf ("%s is owned by user #%ld and not #%ld\n",
						fname, (long)buf.st_uid, (long)uid);
				return (1);
			}

			if (verbose)
				printf ("%s is owned by user #%ld OK\n",
					fname, (long)uid);
		}

		if (gid != (gid_t)-1)
		{
			if (buf.st_gid != gid)
			{
				if (verbose)
					printf ("%s is owned by group #%ld and not #%ld\n",
						fname, (long)buf.st_gid, (long)gid);
				return (1);
			}

			if (verbose)
				printf ("%s is owned by group #%ld OK\n",
					fname, (long)gid);
		}
	} while (++optind < argc);

	return (0);
}