/** * @file apply-default-acl.c * * @brief The command-line interface. * */ #include /* EINVAL */ #include /* AT_FOO constants */ #include /* getopt_long() */ #include /* the "bool" type */ #include /* perror() */ #include /* EXIT_FAILURE, EXIT_SUCCESS */ #include /* faccessat() */ #include "libadacl.h" /* We exit with EXIT_FAILURE for small errors, but we need something * else for big ones. */ #define EXIT_ERROR 2 /* Prototypes */ bool path_accessible(const char* path); void usage(const char* program_name); /** * @brief Determine whether or not the given path is accessible. * * @param path * The path to test. * * @return true if @c path is accessible to the current effective * user/group, false otherwise. */ bool path_accessible(const char* path) { if (path == NULL) { return false; } /* Test for access using the effective user and group rather than the real one. */ int flags = AT_EACCESS; /* Don't follow symlinks when checking for a path's existence, since we won't follow them to set its ACLs either. */ flags |= AT_SYMLINK_NOFOLLOW; /* If the path is relative, interpret it relative to the current working directory (just like the access() system call). */ if (faccessat(AT_FDCWD, path, F_OK, flags) == 0) { return true; } else { return false; } } /** * @brief Display program usage information. * * @param program_name * The program name to use in the output. * */ void usage(const char* program_name) { if (program_name == NULL) { /* ??? */ return; } printf("Apply any applicable default ACLs to the given files or " "directories.\n\n"); printf("Usage: %s [flags] [ [ ...]]\n\n", program_name); printf("Flags:\n"); printf(" -h, --help Print this help message\n"); printf(" -r, --recursive Act on any given directories recursively\n"); return; } /** * @brief Call apply_default_acl (possibly recursively) on each * command-line argument. * * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return * @c EXIT_FAILURE. */ int main(int argc, char* argv[]) { if (argc < 2) { usage(argv[0]); return EXIT_FAILURE; } bool recursive = false; struct option long_options[] = { /* These options set a flag. */ {"help", no_argument, NULL, 'h'}, {"recursive", no_argument, NULL, 'r'}, {NULL, 0, NULL, 0} }; int opt = 0; while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) { switch (opt) { case 'h': usage(argv[0]); return EXIT_SUCCESS; case 'r': recursive = true; break; default: usage(argv[0]); return EXIT_FAILURE; } } int result = EXIT_SUCCESS; int arg_index = 1; int reapp_result = ACL_SUCCESS; for (arg_index = optind; arg_index < argc; arg_index++) { const char* target = argv[arg_index]; /* Make sure we can access the given path before we go out of our * way to please it. Doing this check outside of * apply_default_acl() lets us spit out a better error message for * typos, too. */ if (!path_accessible(target)) { perror(target); result = EXIT_FAILURE; continue; } reapp_result = apply_default_acl(target, recursive); if (result == EXIT_SUCCESS && reapp_result == ACL_FAILURE) { /* We don't want to turn an error into a (less-severe) failure. */ result = EXIT_FAILURE; } if (reapp_result == ACL_ERROR) { /* Turn both success and failure into an error, if we encounter one. */ result = EXIT_ERROR; } } return result; }