| 1 | /* chgrp -- change group ownership of files
|
|---|
| 2 | Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation, Inc.
|
|---|
| 3 |
|
|---|
| 4 | This program is free software; you can redistribute it and/or modify
|
|---|
| 5 | it under the terms of the GNU General Public License as published by
|
|---|
| 6 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 7 | any later version.
|
|---|
| 8 |
|
|---|
| 9 | This program is distributed in the hope that it will be useful,
|
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 12 | GNU General Public License for more details.
|
|---|
| 13 |
|
|---|
| 14 | You should have received a copy of the GNU General Public License
|
|---|
| 15 | along with this program; if not, write to the Free Software Foundation,
|
|---|
| 16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|---|
| 17 |
|
|---|
| 18 | /* Written by David MacKenzie <[email protected]>. */
|
|---|
| 19 |
|
|---|
| 20 | #include <config.h>
|
|---|
| 21 | #include <stdio.h>
|
|---|
| 22 | #include <sys/types.h>
|
|---|
| 23 | #include <grp.h>
|
|---|
| 24 | #include <getopt.h>
|
|---|
| 25 |
|
|---|
| 26 | #include "system.h"
|
|---|
| 27 | #include "chown-core.h"
|
|---|
| 28 | #include "error.h"
|
|---|
| 29 | #include "fts_.h"
|
|---|
| 30 | #include "group-member.h"
|
|---|
| 31 | #include "lchown.h"
|
|---|
| 32 | #include "quote.h"
|
|---|
| 33 | #include "xstrtol.h"
|
|---|
| 34 |
|
|---|
| 35 | /* The official name of this program (e.g., no `g' prefix). */
|
|---|
| 36 | #define PROGRAM_NAME "chgrp"
|
|---|
| 37 |
|
|---|
| 38 | #define AUTHORS "David MacKenzie", "Jim Meyering"
|
|---|
| 39 |
|
|---|
| 40 | #if ! HAVE_ENDGRENT
|
|---|
| 41 | # define endgrent() ((void) 0)
|
|---|
| 42 | #endif
|
|---|
| 43 |
|
|---|
| 44 | /* The name the program was run with. */
|
|---|
| 45 | char *program_name;
|
|---|
| 46 |
|
|---|
| 47 | /* The argument to the --reference option. Use the group ID of this file.
|
|---|
| 48 | This file must exist. */
|
|---|
| 49 | static char *reference_file;
|
|---|
| 50 |
|
|---|
| 51 | /* For long options that have no equivalent short option, use a
|
|---|
| 52 | non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|---|
| 53 | enum
|
|---|
| 54 | {
|
|---|
| 55 | DEREFERENCE_OPTION = CHAR_MAX + 1,
|
|---|
| 56 | REFERENCE_FILE_OPTION
|
|---|
| 57 | };
|
|---|
| 58 |
|
|---|
| 59 | static struct option const long_options[] =
|
|---|
| 60 | {
|
|---|
| 61 | {"recursive", no_argument, NULL, 'R'},
|
|---|
| 62 | {"changes", no_argument, NULL, 'c'},
|
|---|
| 63 | {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
|
|---|
| 64 | {"no-dereference", no_argument, NULL, 'h'},
|
|---|
| 65 | {"quiet", no_argument, NULL, 'f'},
|
|---|
| 66 | {"silent", no_argument, NULL, 'f'},
|
|---|
| 67 | {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
|
|---|
| 68 | {"verbose", no_argument, NULL, 'v'},
|
|---|
| 69 | {GETOPT_HELP_OPTION_DECL},
|
|---|
| 70 | {GETOPT_VERSION_OPTION_DECL},
|
|---|
| 71 | {NULL, 0, NULL, 0}
|
|---|
| 72 | };
|
|---|
| 73 |
|
|---|
| 74 | /* Return the group ID of NAME, or -1 if no name was specified. */
|
|---|
| 75 |
|
|---|
| 76 | static gid_t
|
|---|
| 77 | parse_group (const char *name)
|
|---|
| 78 | {
|
|---|
| 79 | gid_t gid = -1;
|
|---|
| 80 |
|
|---|
| 81 | if (*name)
|
|---|
| 82 | {
|
|---|
| 83 | struct group *grp = getgrnam (name);
|
|---|
| 84 | if (grp)
|
|---|
| 85 | gid = grp->gr_gid;
|
|---|
| 86 | else
|
|---|
| 87 | {
|
|---|
| 88 | unsigned long int tmp;
|
|---|
| 89 | if (! (xstrtoul (name, NULL, 10, &tmp, "") == LONGINT_OK
|
|---|
| 90 | && tmp <= GID_T_MAX))
|
|---|
| 91 | error (EXIT_FAILURE, 0, _("invalid group %s"), quote (name));
|
|---|
| 92 | gid = tmp;
|
|---|
| 93 | }
|
|---|
| 94 | endgrent (); /* Save a file descriptor. */
|
|---|
| 95 | }
|
|---|
| 96 |
|
|---|
| 97 | return gid;
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | void
|
|---|
| 101 | usage (int status)
|
|---|
| 102 | {
|
|---|
| 103 | if (status != EXIT_SUCCESS)
|
|---|
| 104 | fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|---|
| 105 | program_name);
|
|---|
| 106 | else
|
|---|
| 107 | {
|
|---|
| 108 | printf (_("\
|
|---|
| 109 | Usage: %s [OPTION]... GROUP FILE...\n\
|
|---|
| 110 | or: %s [OPTION]... --reference=RFILE FILE...\n\
|
|---|
| 111 | "),
|
|---|
| 112 | program_name, program_name);
|
|---|
| 113 | fputs (_("\
|
|---|
| 114 | Change the group of each FILE to GROUP.\n\
|
|---|
| 115 | With --reference, change the group of each FILE to that of RFILE.\n\
|
|---|
| 116 | \n\
|
|---|
| 117 | -c, --changes like verbose but report only when a change is made\n\
|
|---|
| 118 | --dereference affect the referent of each symbolic link, rather\n\
|
|---|
| 119 | than the symbolic link itself (this is the default)\n\
|
|---|
| 120 | "), stdout);
|
|---|
| 121 | fputs (_("\
|
|---|
| 122 | -h, --no-dereference affect each symbolic link instead of any referenced\n\
|
|---|
| 123 | file (useful only on systems that can change the\n\
|
|---|
| 124 | ownership of a symlink)\n\
|
|---|
| 125 | "), stdout);
|
|---|
| 126 | fputs (_("\
|
|---|
| 127 | --no-preserve-root do not treat `/' specially (the default)\n\
|
|---|
| 128 | --preserve-root fail to operate recursively on `/'\n\
|
|---|
| 129 | "), stdout);
|
|---|
| 130 | fputs (_("\
|
|---|
| 131 | -f, --silent, --quiet suppress most error messages\n\
|
|---|
| 132 | --reference=RFILE use RFILE's group rather than the specifying\n\
|
|---|
| 133 | GROUP value\n\
|
|---|
| 134 | -R, --recursive operate on files and directories recursively\n\
|
|---|
| 135 | -v, --verbose output a diagnostic for every file processed\n\
|
|---|
| 136 | \n\
|
|---|
| 137 | "), stdout);
|
|---|
| 138 | fputs (_("\
|
|---|
| 139 | The following options modify how a hierarchy is traversed when the -R\n\
|
|---|
| 140 | option is also specified. If more than one is specified, only the final\n\
|
|---|
| 141 | one takes effect.\n\
|
|---|
| 142 | \n\
|
|---|
| 143 | -H if a command line argument is a symbolic link\n\
|
|---|
| 144 | to a directory, traverse it\n\
|
|---|
| 145 | -L traverse every symbolic link to a directory\n\
|
|---|
| 146 | encountered\n\
|
|---|
| 147 | -P do not traverse any symbolic links (default)\n\
|
|---|
| 148 | \n\
|
|---|
| 149 | "), stdout);
|
|---|
| 150 | fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|---|
| 151 | fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
|---|
| 152 | printf (_("\
|
|---|
| 153 | \n\
|
|---|
| 154 | Examples:\n\
|
|---|
| 155 | %s staff /u Change the group of /u to \"staff\".\n\
|
|---|
| 156 | %s -hR staff /u Change the group of /u and subfiles to \"staff\".\n\
|
|---|
| 157 | "),
|
|---|
| 158 | program_name, program_name);
|
|---|
| 159 | printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
|
|---|
| 160 | }
|
|---|
| 161 | exit (status);
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | int
|
|---|
| 165 | main (int argc, char **argv)
|
|---|
| 166 | {
|
|---|
| 167 | gid_t gid;
|
|---|
| 168 |
|
|---|
| 169 | /* Bit flags that control how fts works. */
|
|---|
| 170 | int bit_flags = FTS_PHYSICAL;
|
|---|
| 171 |
|
|---|
| 172 | /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
|
|---|
| 173 | specified. */
|
|---|
| 174 | int dereference = -1;
|
|---|
| 175 |
|
|---|
| 176 | struct Chown_option chopt;
|
|---|
| 177 | bool ok;
|
|---|
| 178 | int optc;
|
|---|
| 179 |
|
|---|
| 180 | initialize_main (&argc, &argv);
|
|---|
| 181 | program_name = argv[0];
|
|---|
| 182 | setlocale (LC_ALL, "");
|
|---|
| 183 | bindtextdomain (PACKAGE, LOCALEDIR);
|
|---|
| 184 | textdomain (PACKAGE);
|
|---|
| 185 |
|
|---|
| 186 | atexit (close_stdout);
|
|---|
| 187 |
|
|---|
| 188 | chopt_init (&chopt);
|
|---|
| 189 |
|
|---|
| 190 | while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, NULL))
|
|---|
| 191 | != -1)
|
|---|
| 192 | {
|
|---|
| 193 | switch (optc)
|
|---|
| 194 | {
|
|---|
| 195 | case 'H': /* Traverse command-line symlinks-to-directories. */
|
|---|
| 196 | bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
|
|---|
| 197 | break;
|
|---|
| 198 |
|
|---|
| 199 | case 'L': /* Traverse all symlinks-to-directories. */
|
|---|
| 200 | bit_flags = FTS_LOGICAL;
|
|---|
| 201 | break;
|
|---|
| 202 |
|
|---|
| 203 | case 'P': /* Traverse no symlinks-to-directories. */
|
|---|
| 204 | bit_flags = FTS_PHYSICAL;
|
|---|
| 205 | break;
|
|---|
| 206 |
|
|---|
| 207 | case 'h': /* --no-dereference: affect symlinks */
|
|---|
| 208 | dereference = 0;
|
|---|
| 209 | break;
|
|---|
| 210 |
|
|---|
| 211 | case DEREFERENCE_OPTION: /* --dereference: affect the referent
|
|---|
| 212 | of each symlink */
|
|---|
| 213 | dereference = 1;
|
|---|
| 214 | break;
|
|---|
| 215 |
|
|---|
| 216 | case REFERENCE_FILE_OPTION:
|
|---|
| 217 | reference_file = optarg;
|
|---|
| 218 | break;
|
|---|
| 219 |
|
|---|
| 220 | case 'R':
|
|---|
| 221 | chopt.recurse = true;
|
|---|
| 222 | break;
|
|---|
| 223 |
|
|---|
| 224 | case 'c':
|
|---|
| 225 | chopt.verbosity = V_changes_only;
|
|---|
| 226 | break;
|
|---|
| 227 |
|
|---|
| 228 | case 'f':
|
|---|
| 229 | chopt.force_silent = true;
|
|---|
| 230 | break;
|
|---|
| 231 |
|
|---|
| 232 | case 'v':
|
|---|
| 233 | chopt.verbosity = V_high;
|
|---|
| 234 | break;
|
|---|
| 235 |
|
|---|
| 236 | case_GETOPT_HELP_CHAR;
|
|---|
| 237 | case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|---|
| 238 | default:
|
|---|
| 239 | usage (EXIT_FAILURE);
|
|---|
| 240 | }
|
|---|
| 241 | }
|
|---|
| 242 |
|
|---|
| 243 | if (chopt.recurse)
|
|---|
| 244 | {
|
|---|
| 245 | if (bit_flags == FTS_PHYSICAL)
|
|---|
| 246 | {
|
|---|
| 247 | if (dereference == 1)
|
|---|
| 248 | error (EXIT_FAILURE, 0,
|
|---|
| 249 | _("-R --dereference requires either -H or -L"));
|
|---|
| 250 | chopt.affect_symlink_referent = false;
|
|---|
| 251 | }
|
|---|
| 252 | else
|
|---|
| 253 | {
|
|---|
| 254 | if (dereference == 0)
|
|---|
| 255 | error (EXIT_FAILURE, 0, _("-R -h requires -P"));
|
|---|
| 256 | chopt.affect_symlink_referent = true;
|
|---|
| 257 | }
|
|---|
| 258 | }
|
|---|
| 259 | else
|
|---|
| 260 | {
|
|---|
| 261 | bit_flags = FTS_PHYSICAL;
|
|---|
| 262 | chopt.affect_symlink_referent = (dereference != 0);
|
|---|
| 263 | }
|
|---|
| 264 |
|
|---|
| 265 | if (argc - optind < (reference_file ? 1 : 2))
|
|---|
| 266 | {
|
|---|
| 267 | if (argc <= optind)
|
|---|
| 268 | error (0, 0, _("missing operand"));
|
|---|
| 269 | else
|
|---|
| 270 | error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
|
|---|
| 271 | usage (EXIT_FAILURE);
|
|---|
| 272 | }
|
|---|
| 273 |
|
|---|
| 274 | if (reference_file)
|
|---|
| 275 | {
|
|---|
| 276 | struct stat ref_stats;
|
|---|
| 277 | if (stat (reference_file, &ref_stats))
|
|---|
| 278 | error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
|
|---|
| 279 | quote (reference_file));
|
|---|
| 280 |
|
|---|
| 281 | gid = ref_stats.st_gid;
|
|---|
| 282 | chopt.group_name = gid_to_name (ref_stats.st_gid);
|
|---|
| 283 | }
|
|---|
| 284 | else
|
|---|
| 285 | {
|
|---|
| 286 | char *group_name = argv[optind++];
|
|---|
| 287 | chopt.group_name = (*group_name ? group_name : NULL);
|
|---|
| 288 | gid = parse_group (group_name);
|
|---|
| 289 | }
|
|---|
| 290 |
|
|---|
| 291 | ok = chown_files (argv + optind, bit_flags,
|
|---|
| 292 | (uid_t) -1, gid,
|
|---|
| 293 | (uid_t) -1, (gid_t) -1, &chopt);
|
|---|
| 294 |
|
|---|
| 295 | chopt_free (&chopt);
|
|---|
| 296 |
|
|---|
| 297 | exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
|---|
| 298 | }
|
|---|