1 /*- 2 * Copyright (C) 1996 3 * David L. Nugent. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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 * 14 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include "pw.h" 30 #include <paths.h> 31 #include <sys/wait.h> 32 33 static char *progname = "pw"; 34 35 const char *Modes[] = {"add", "del", "mod", "show", "next", NULL}; 36 const char *Which[] = {"user", "group", NULL}; 37 static const char *Combo1[] = { 38 "useradd", "userdel", "usermod", "usershow", "usernext", 39 "groupadd", "groupdel", "groupmod", "groupshow", "groupnext", 40 NULL}; 41 static const char *Combo2[] = { 42 "adduser", "deluser", "moduser", "showuser", "nextuser", 43 "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup", 44 NULL}; 45 46 static struct cargs arglist; 47 48 static int getindex(const char *words[], const char *word); 49 static void cmdhelp(int mode, int which); 50 51 52 int 53 main(int argc, char *argv[]) 54 { 55 int ch; 56 int mode = -1; 57 int which = -1; 58 struct userconf *cnf; 59 60 static const char *opts[W_NUM][M_NUM] = 61 { 62 { /* user */ 63 "C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db:NPy:Y", 64 "C:qn:u:rY", 65 "C:qn:u:c:d:e:p:g:G:ml:k:s:w:L:h:FNPY", 66 "C:qn:u:FPa", 67 "C:q" 68 }, 69 { /* grp */ 70 "C:qn:g:h:M:pNPY", 71 "C:qn:g:Y", 72 "C:qn:g:l:h:FM:m:NPY", 73 "C:qn:g:FPa", 74 "C:q" 75 } 76 }; 77 78 static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) = 79 { /* Request handlers */ 80 pw_user, 81 pw_group 82 }; 83 84 umask(0); /* We wish to handle this manually */ 85 progname = strrchr(argv[0], '/'); 86 if (progname != NULL) 87 ++progname; 88 else 89 progname = argv[0]; 90 91 LIST_INIT(&arglist); 92 93 /* 94 * Break off the first couple of words to determine what exactly 95 * we're being asked to do 96 */ 97 while (argc > 1 && *argv[1] != '-') { 98 int tmp; 99 100 if ((tmp = getindex(Modes, argv[1])) != -1) 101 mode = tmp; 102 else if ((tmp = getindex(Which, argv[1])) != -1) 103 which = tmp; 104 else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) { 105 which = tmp / M_NUM; 106 mode = tmp % M_NUM; 107 } else if (strcmp(argv[1], "help") == 0) 108 cmdhelp(mode, which); 109 else if (which != -1 && mode != -1 && arglist.lh_first == NULL) 110 addarg(&arglist, 'n', argv[1]); 111 else 112 cmderr(EX_USAGE, "Unknown keyword `%s'\n", argv[1]); 113 ++argv; 114 --argc; 115 } 116 117 /* 118 * Bail out unless the user is specific! 119 */ 120 if (mode == -1 || which == -1) 121 cmdhelp(mode, which); 122 123 /* 124 * We know which mode we're in and what we're about to do, so now 125 * let's dispatch the remaining command line args in a genric way. 126 */ 127 argv[0] = progname; /* Preserve this */ 128 optarg = NULL; 129 130 while ((ch = getopt(argc, argv, opts[which][mode])) != -1) { 131 if (ch == '?') 132 cmderr(EX_USAGE, NULL); 133 else 134 addarg(&arglist, ch, optarg); 135 optarg = NULL; 136 } 137 138 /* 139 * Must be root to attempt an update 140 */ 141 if (getuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL) 142 cmderr(EX_NOPERM, "you must be root to run this program\n"); 143 144 /* 145 * We should immediately look for the -q 'quiet' switch so that we 146 * don't bother with extraneous errors 147 */ 148 if (getarg(&arglist, 'q') != NULL) 149 freopen("/dev/null", "w", stderr); 150 151 /* 152 * Now, let's do the common initialisation 153 */ 154 cnf = read_userconfig(getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL); 155 ch = funcs[which] (cnf, mode, &arglist); 156 157 /* 158 * If everything went ok, and we've been asked to update 159 * the NIS maps, then do it now 160 */ 161 if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) { 162 pid_t pid; 163 164 fflush(NULL); 165 if (chdir(_PATH_YP) == -1) 166 perror("chdir(" _PATH_YP ")"); 167 else if ((pid = fork()) == -1) 168 perror("fork()"); 169 else if (pid == 0) { 170 /* Is make anywhere else? */ 171 execlp("/usr/bin/make", "make", NULL); 172 _exit(1); 173 } else { 174 int i; 175 waitpid(pid, &i, 0); 176 if ((i = WEXITSTATUS(i)) != 0) 177 cmderr(ch, "warning: make exited with status %d\n", i); 178 else 179 pw_log(cnf, mode, which, "NIS maps updated"); 180 } 181 } 182 return ch; 183 } 184 185 static int 186 getindex(const char *words[], const char *word) 187 { 188 int i = 0; 189 190 while (words[i]) { 191 if (strcmp(words[i], word) == 0) 192 return i; 193 i++; 194 } 195 return -1; 196 } 197 198 199 /* 200 * This is probably an overkill for a cmdline help system, but it reflects 201 * the complexity of the command line. 202 */ 203 204 static void 205 banner(void) 206 { 207 fprintf(stderr, "%s: ", progname); 208 } 209 210 void 211 cmderr(int ec, char const * fmt,...) 212 { 213 if (fmt != NULL) { 214 va_list argp; 215 216 banner(); 217 va_start(argp, fmt); 218 vfprintf(stderr, fmt, argp); 219 va_end(argp); 220 } 221 exit(ec); 222 } 223 224 static void 225 cmdhelp(int mode, int which) 226 { 227 banner(); 228 if (which == -1) 229 fprintf(stderr, "usage: %s [user|group] [add|del|mod|show|next] [ help | switches/values ]\n", progname); 230 else if (mode == -1) 231 fprintf(stderr, "usage: %s %s [add|del|mod|show|next] [ help | switches/values ]\n", progname, Which[which]); 232 else { 233 234 /* 235 * We need to give mode specific help 236 */ 237 static const char *help[W_NUM][M_NUM] = 238 { 239 { 240 "usage: %s useradd [name] [switches]\n" 241 "\t-C config configuration file\n" 242 "\t-q quiet operation\n" 243 " Adding users:\n" 244 "\t-n name login name\n" 245 "\t-u uid user id\n" 246 "\t-c comment user name/comment\n" 247 "\t-d directory home directory\n" 248 "\t-e date account expiry date\n" 249 "\t-p date password expiry date\n" 250 "\t-g grp initial group\n" 251 "\t-G grp1,grp2 additional groups\n" 252 "\t-m [ -k dir ] create and set up home\n" 253 "\t-s shell name of login shell\n" 254 "\t-o duplicate uid ok\n" 255 "\t-L class user class\n" 256 "\t-h fd read password on fd\n" 257 "\t-Y update NIS maps\n" 258 "\t-N no update\n" 259 " Setting defaults:\n" 260 "\t-D set user defaults\n" 261 "\t-b dir default home root dir\n" 262 "\t-e period default expiry period\n" 263 "\t-p period default password change period\n" 264 "\t-g group default group\n" 265 "\t-G grp1,grp2 additional groups\n" 266 "\t-L class default user class\n" 267 "\t-k dir default home skeleton\n" 268 "\t-u min,max set min,max uids\n" 269 "\t-i min,max set min,max gids\n" 270 "\t-w method set default password method\n" 271 "\t-s shell default shell\n" 272 "\t-y path set NIS passwd file path\n", 273 "usage: %s userdel [uid|name] [switches]\n" 274 "\t-n name login name\n" 275 "\t-u uid user id\n" 276 "\t-Y update NIS maps\n" 277 "\t-r remove home & contents\n", 278 "usage: %s usermod [uid|name] [switches]\n" 279 "\t-C config configuration file\n" 280 "\t-q quiet operation\n" 281 "\t-F force add if no user\n" 282 "\t-n name login name\n" 283 "\t-u uid user id\n" 284 "\t-c comment user name/comment\n" 285 "\t-d directory home directory\n" 286 "\t-e date account expiry date\n" 287 "\t-p date password expiry date\n" 288 "\t-g grp initial group\n" 289 "\t-G grp1,grp2 additional groups\n" 290 "\t-l name new login name\n" 291 "\t-L class user class\n" 292 "\t-m [ -k dir ] create and set up home\n" 293 "\t-s shell name of login shell\n" 294 "\t-w method set new password using method\n" 295 "\t-h fd read password on fd\n" 296 "\t-Y update NIS maps\n" 297 "\t-N no update\n", 298 "usage: %s usershow [uid|name] [switches]\n" 299 "\t-n name login name\n" 300 "\t-u uid user id\n" 301 "\t-F force print\n" 302 "\t-P prettier format\n" 303 "\t-a print all users\n", 304 "usage: %s usernext [switches]\n" 305 "\t-C config configuration file\n" 306 }, 307 { 308 "usage: %s groupadd [group|gid] [switches]\n" 309 "\t-C config configuration file\n" 310 "\t-q quiet operation\n" 311 "\t-n group group name\n" 312 "\t-g gid group id\n" 313 "\t-M usr1,usr2 add users as group members\n" 314 "\t-o duplicate gid ok\n" 315 "\t-Y update NIS maps\n" 316 "\t-N no update\n", 317 "usage: %s groupdel [group|gid] [switches]\n" 318 "\t-n name group name\n" 319 "\t-g gid group id\n" 320 "\t-Y update NIS maps\n", 321 "usage: %s groupmod [group|gid] [switches]\n" 322 "\t-C config configuration file\n" 323 "\t-q quiet operation\n" 324 "\t-F force add if not exists\n" 325 "\t-n name group name\n" 326 "\t-g gid group id\n" 327 "\t-M usr1,usr2 replaces users as group members\n" 328 "\t-m usr1,usr2 add users as group members\n" 329 "\t-l name new group name\n" 330 "\t-Y update NIS maps\n" 331 "\t-N no update\n", 332 "usage: %s groupshow [group|gid] [switches]\n" 333 "\t-n name group name\n" 334 "\t-g gid group id\n" 335 "\t-F force print\n" 336 "\t-P prettier format\n" 337 "\t-a print all accounting groups\n", 338 "usage: %s groupnext [switches]\n" 339 "\t-C config configuration file\n" 340 } 341 }; 342 343 fprintf(stderr, help[which][mode], progname); 344 } 345 exit(EXIT_FAILURE); 346 } 347 348 struct carg * 349 getarg(struct cargs * _args, int ch) 350 { 351 struct carg *c = _args->lh_first; 352 353 while (c != NULL && c->ch != ch) 354 c = c->list.le_next; 355 return c; 356 } 357 358 struct carg * 359 addarg(struct cargs * _args, int ch, char *argstr) 360 { 361 struct carg *ca = malloc(sizeof(struct carg)); 362 363 if (ca == NULL) 364 cmderr(EX_OSERR, "Abort - out of memory\n"); 365 ca->ch = ch; 366 ca->val = argstr; 367 LIST_INSERT_HEAD(_args, ca, list); 368 return ca; 369 } 370