2 * Copyright 1991 - 1993, Julianne Frances Haugh
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 RCSID(PKG_VER "$Id: groupadd.c,v 1.14 1999/06/07 16:40:45 marekm Exp $")
35 #include <sys/types.h>
42 #include "prototypes.h"
52 static int is_shadow_grp;
59 #define E_SUCCESS 0 /* success */
60 #define E_USAGE 2 /* invalid command syntax */
61 #define E_BAD_ARG 3 /* invalid argument to option */
62 #define E_GID_IN_USE 4 /* gid not unique (when -o not used) */
63 #define E_NAME_IN_USE 9 /* group name nut unique */
64 #define E_GRP_UPDATE 10 /* can't update group file */
66 static char *group_name;
67 static gid_t group_id;
68 static char *empty_list = NULL;
72 static int oflg = 0; /* permit non-unique group ID to be specified with -g */
73 static int gflg = 0; /* ID value for the new group */
74 static int fflg = 0; /* if group already exists, do nothing and exit(0) */
77 extern int gr_dbm_mode;
78 extern int sg_dbm_mode;
84 /* local function prototypes */
85 static void usage P_((void));
86 static void new_grent P_((struct group *));
88 static void new_sgent P_((struct sgrp *));
90 static void grp_update P_((void));
91 static void find_new_gid P_((void));
92 static void check_new_name P_((void));
93 static void process_flags P_((int, char **));
94 static void close_files P_((void));
95 static void open_files P_((void));
96 static void fail_exit P_((int));
97 int main P_((int, char **));
100 * usage - display usage message and exit
106 fprintf(stderr, _("usage: groupadd [-g gid [-o]] group\n"));
111 * new_grent - initialize the values in a group file entry
113 * new_grent() takes all of the values that have been entered and
114 * fills in a (struct group) with them.
118 new_grent(struct group *grent)
120 memzero(grent, sizeof *grent);
121 grent->gr_name = group_name;
122 grent->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
123 grent->gr_gid = group_id;
124 grent->gr_mem = &empty_list;
129 * new_sgent - initialize the values in a shadow group file entry
131 * new_sgent() takes all of the values that have been entered and
132 * fills in a (struct sgrp) with them.
136 new_sgent(struct sgrp *sgent)
138 memzero(sgent, sizeof *sgent);
139 sgent->sg_name = group_name;
140 sgent->sg_passwd = "!"; /* XXX warning: const */
141 sgent->sg_adm = &empty_list;
142 sgent->sg_mem = &empty_list;
144 #endif /* SHADOWGRP */
147 * grp_update - add new group file entries
149 * grp_update() writes the new records to the group files.
158 #endif /* SHADOWGRP */
161 * Create the initial entries for this new group.
167 #endif /* SHADOWGRP */
170 * Write out the new group file entry.
173 if (! gr_update (&grp)) {
174 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
175 fail_exit(E_GRP_UPDATE);
180 * Update the DBM group file with the new entry as well.
183 if (gr_dbm_present() && ! gr_dbm_update (&grp)) {
184 fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
185 fail_exit(E_GRP_UPDATE);
193 * Write out the new shadow group entries as well.
196 if (is_shadow_grp && ! sgr_update (&sgrp)) {
197 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
198 fail_exit(E_GRP_UPDATE);
203 * Update the DBM group file with the new entry as well.
206 if (is_shadow_grp && sg_dbm_present() && ! sg_dbm_update (&sgrp)) {
207 fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
208 fail_exit(E_GRP_UPDATE);
212 #endif /* SHADOWGRP */
213 SYSLOG((LOG_INFO, "new group: name=%s, gid=%d\n",
214 group_name, group_id));
218 * find_new_gid - find the next available GID
220 * find_new_gid() locates the next highest unused GID in the group
221 * file, or checks the given group ID against the existing ones for
228 const struct group *grp;
229 gid_t gid_min, gid_max;
231 gid_min = getdef_num("GID_MIN", 100);
232 gid_max = getdef_num("GID_MAX", 60000);
235 * Start with some GID value if the user didn't provide us with
243 * Search the entire group file, either looking for this
244 * GID (if the user specified one with -g) or looking for the
245 * largest unused value.
250 while ((grp = gr_next())) {
253 while ((grp = getgrent())) {
255 if (strcmp(group_name, grp->gr_name) == 0) {
257 fail_exit(E_SUCCESS);
259 fprintf(stderr, _("%s: name %s is not unique\n"),
261 fail_exit(E_NAME_IN_USE);
263 if (gflg && group_id == grp->gr_gid) {
265 /* turn off -g and search again */
274 fprintf(stderr, _("%s: gid %ld is not unique\n"),
275 Prog, (long) group_id);
276 fail_exit(E_GID_IN_USE);
278 if (! gflg && grp->gr_gid >= group_id) {
279 if (grp->gr_gid > gid_max)
281 group_id = grp->gr_gid + 1;
284 if (!gflg && group_id == gid_max + 1) {
285 for (group_id = gid_min; group_id < gid_max; group_id++) {
288 while ((grp = gr_next()) && grp->gr_gid != group_id)
293 if (!getgrgid(group_id))
297 if (group_id == gid_max) {
298 fprintf(stderr, _("%s: can't get unique gid\n"),
300 fail_exit(E_GID_IN_USE);
306 * check_new_name - check the new name for validity
308 * check_new_name() insures that the new name doesn't contain
309 * any illegal characters.
315 if (check_group_name(group_name))
319 * All invalid group names land here.
322 fprintf(stderr, _("%s: %s is a not a valid group name\n"),
329 * process_flags - perform command line argument setting
331 * process_flags() interprets the command line arguments and sets
332 * the values that the user will be created with accordingly. The
333 * values are checked for sanity.
337 process_flags(int argc, char **argv)
342 while ((arg = getopt(argc, argv, "og:O:f")) != EOF) {
346 if (! isdigit (optarg[0]))
349 group_id = strtol(optarg, &cp, 10);
351 fprintf(stderr, _("%s: invalid group %s\n"),
353 fail_exit(E_BAD_ARG);
361 * override login.defs defaults (-O name=value)
362 * example: -O GID_MIN=100 -O GID_MAX=499
363 * note: -O GID_MIN=10,GID_MAX=499 doesn't work yet
365 cp = strchr(optarg, '=');
368 _("%s: -O requires NAME=VALUE\n"),
372 /* terminate name, point to value */
374 if (putdef_str(optarg, cp) < 0)
379 * "force" - do nothing, just exit(0), if the
380 * specified group already exists. With -g, if
381 * specified gid already exists, choose another
382 * (unique) gid (turn off -g). Based on the
383 * RedHat's patch from shadow-utils-970616-9.
395 if (optind != argc - 1)
398 group_name = argv[argc - 1];
403 * close_files - close all of the files that were opened
405 * close_files() closes all of the files that were opened for this
406 * new group. This causes any modified entries to be written out.
413 fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
414 fail_exit(E_GRP_UPDATE);
418 if (is_shadow_grp && !sgr_close()) {
419 fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
421 fail_exit(E_GRP_UPDATE);
425 #endif /* SHADOWGRP */
429 * open_files - lock and open the group files
431 * open_files() opens the two group files.
438 fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
441 if (! gr_open (O_RDWR)) {
442 fprintf(stderr, _("%s: unable to open group file\n"), Prog);
443 fail_exit(E_GRP_UPDATE);
446 if (is_shadow_grp && ! sgr_lock ()) {
447 fprintf(stderr, _("%s: unable to lock shadow group file\n"),
449 fail_exit(E_GRP_UPDATE);
451 if (is_shadow_grp && ! sgr_open (O_RDWR)) {
452 fprintf(stderr, _("%s: unable to open shadow group file\n"),
454 fail_exit(E_GRP_UPDATE);
456 #endif /* SHADOWGRP */
460 * fail_exit - exit with an error code after unlocking files
475 * main - groupadd command
479 main(int argc, char **argv)
483 * Get my name so that I can use it to report errors.
486 Prog = Basename(argv[0]);
488 setlocale(LC_ALL, "");
489 bindtextdomain(PACKAGE, LOCALEDIR);
492 openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
495 is_shadow_grp = sgr_file_present();
499 * The open routines for the DBM files don't use read-write
500 * as the mode, so we have to clue them in.
504 gr_dbm_mode = O_RDWR;
506 sg_dbm_mode = O_RDWR;
507 #endif /* SHADOWGRP */
509 process_flags(argc, argv);
512 * Start with a quick check to see if the group exists.
515 if (getgrnam(group_name)) {
519 fprintf(stderr, _("%s: group %s exists\n"), Prog, group_name);
524 * Do the hard stuff - open the files, create the group entries,
525 * then close and update the files.