xref: /illumos-gate/usr/src/cmd/oamuser/user/useradd.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2013 Gary Mills
23  *
24  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * Copyright (c) 2013 RackTop Systems.
33  */
34 
35 #include	<sys/types.h>
36 #include	<sys/stat.h>
37 #include	<sys/param.h>
38 #include	<stdio.h>
39 #include	<stdlib.h>
40 #include	<ctype.h>
41 #include	<limits.h>
42 #include	<string.h>
43 #include	<userdefs.h>
44 #include	<errno.h>
45 #include	<project.h>
46 #include	<unistd.h>
47 #include	<user_attr.h>
48 #include	<libcmdutils.h>
49 #include	"users.h"
50 #include	"messages.h"
51 #include	"userdisp.h"
52 #include	"funcs.h"
53 
54 /*
55  *  useradd [-u uid [-o] | -g group | -G group [[, group]...] | -d dir [-m]
56  *		| -s shell | -c comment | -k skel_dir | -b base_dir] ]
57  *		[ -A authorization [, authorization ...]]
58  *		[ -P profile [, profile ...]]
59  *		[ -K key=value ]
60  *		[ -R role [, role ...]] [-p project [, project ...]] login
61  *  useradd -D [ -g group ] [ -b base_dir | -f inactive | -e expire |
62  *		-s shell | -k skel_dir ]
63  *		[ -A authorization [, authorization ...]]
64  *		[ -P profile [, profile ...]] [ -K key=value ]
65  *		[ -R role [, role ...]] [-p project [, project ...]] login
66  *
67  *	This command adds new user logins to the system.  Arguments are:
68  *
69  *	uid - an integer
70  *	group - an existing group's integer ID or char string name
71  *	dir - home directory
72  *	shell - a program to be used as a shell
73  *	comment - any text string
74  *	skel_dir - a skeleton directory
75  *	base_dir - a directory
76  *	login - a string of printable chars except colon(:)
77  *	authorization - One or more comma separated authorizations defined
78  *			in auth_attr(4).
79  *	profile - One or more comma separated execution profiles defined
80  *		  in prof_attr(4)
81  *	role - One or more comma-separated role names defined in user_attr(4)
82  *	project - One or more comma-separated project names or numbers
83  *
84  */
85 
86 extern struct userdefs *getusrdef();
87 extern void dispusrdef();
88 
89 static void cleanup();
90 
91 extern int check_perm(), valid_expire();
92 extern int putusrdef(), valid_uid();
93 extern int call_passmgmt(), edit_group(), create_home();
94 extern int edit_project();
95 extern int **valid_lgroup();
96 extern projid_t **valid_lproject();
97 extern void update_def(struct userdefs *);
98 extern void import_def(struct userdefs *);
99 
100 static uid_t uid;			/* new uid */
101 static char *logname;			/* login name to add */
102 static struct userdefs *usrdefs;	/* defaults for useradd */
103 
104 char *cmdname;
105 
106 static char homedir[ PATH_MAX + 1 ];	/* home directory */
107 static char gidstring[32];		/* group id string representation */
108 static gid_t gid;			/* gid of new login */
109 static char uidstring[32];		/* user id string representation */
110 static char *uidstr = NULL;		/* uid from command line */
111 static char *base_dir = NULL;		/* base_dir from command line */
112 static char *group = NULL;		/* group from command line */
113 static char *grps = NULL;		/* multi groups from command line */
114 static char *dir = NULL;		/* home dir from command line */
115 static char *shell = NULL;		/* shell from command line */
116 static char *comment = NULL;		/* comment from command line */
117 static char *skel_dir = NULL;		/* skel dir from command line */
118 static long inact;			/* inactive days */
119 static char *inactstr = NULL;		/* inactive from command line */
120 static char inactstring[10];		/* inactivity string representation */
121 static char *expirestr = NULL;		/* expiration date from command line */
122 static char *projects = NULL;		/* project id's from command line */
123 
124 static char *usertype = NULL;	/* type of user, either role or normal */
125 
126 typedef enum {
127 	BASEDIR	= 0,
128 	SKELDIR,
129 	SHELL
130 } path_opt_t;
131 
132 
133 static void valid_input(path_opt_t, const char *);
134 
135 int
136 main(argc, argv)
137 int argc;
138 char *argv[];
139 {
140 	int ch, ret, mflag = 0, oflag = 0, Dflag = 0, **gidlist = NULL;
141 	projid_t **projlist = NULL;
142 	char *ptr;			/* loc in a str, may be set by strtol */
143 	struct group *g_ptr;
144 	struct project p_ptr;
145 	char mybuf[PROJECT_BUFSZ];
146 	struct stat statbuf;		/* status buffer for stat */
147 	int warning;
148 	int busy = 0;
149 	char **nargv;			/* arguments for execvp of passmgmt */
150 	int argindex;			/* argument index into nargv */
151 
152 	cmdname = argv[0];
153 
154 	if (geteuid() != 0) {
155 		errmsg(M_PERM_DENIED);
156 		exit(EX_NO_PERM);
157 	}
158 
159 	opterr = 0;			/* no print errors from getopt */
160 	usertype = getusertype(argv[0]);
161 
162 	change_key(USERATTR_TYPE_KW, usertype);
163 
164 	while ((ch = getopt(argc, argv,
165 		    "b:c:Dd:e:f:G:g:k:mop:s:u:A:P:R:K:")) != EOF)
166 		switch (ch) {
167 		case 'b':
168 			base_dir = optarg;
169 			break;
170 
171 		case 'c':
172 			comment = optarg;
173 			break;
174 
175 		case 'D':
176 			Dflag++;
177 			break;
178 
179 		case 'd':
180 			dir = optarg;
181 			break;
182 
183 		case 'e':
184 			expirestr = optarg;
185 			break;
186 
187 		case 'f':
188 			inactstr = optarg;
189 			break;
190 
191 		case 'G':
192 			grps = optarg;
193 			break;
194 
195 		case 'g':
196 			group = optarg;
197 			break;
198 
199 		case 'k':
200 			skel_dir = optarg;
201 			break;
202 
203 		case 'm':
204 			mflag++;
205 			break;
206 
207 		case 'o':
208 			oflag++;
209 			break;
210 
211 		case 'p':
212 			projects = optarg;
213 			break;
214 
215 		case 's':
216 			shell = optarg;
217 			break;
218 
219 		case 'u':
220 			uidstr = optarg;
221 			break;
222 
223 		case 'A':
224 			change_key(USERATTR_AUTHS_KW, optarg);
225 			break;
226 
227 		case 'P':
228 			change_key(USERATTR_PROFILES_KW, optarg);
229 			break;
230 
231 		case 'R':
232 			if (is_role(usertype)) {
233 				errmsg(M_ARUSAGE);
234 				exit(EX_SYNTAX);
235 			}
236 			change_key(USERATTR_ROLES_KW, optarg);
237 			break;
238 
239 		case 'K':
240 			change_key(NULL, optarg);
241 			break;
242 
243 		default:
244 		case '?':
245 			if (is_role(usertype))
246 				errmsg(M_ARUSAGE);
247 			else
248 				errmsg(M_AUSAGE);
249 			exit(EX_SYNTAX);
250 		}
251 
252 	/* get defaults for adding new users */
253 	usrdefs = getusrdef(usertype);
254 
255 	if (Dflag) {
256 		/* DISPLAY mode */
257 
258 		/* check syntax */
259 		if (optind != argc) {
260 			if (is_role(usertype))
261 				errmsg(M_ARUSAGE);
262 			else
263 				errmsg(M_AUSAGE);
264 			exit(EX_SYNTAX);
265 		}
266 
267 		if (uidstr != NULL || oflag || grps != NULL ||
268 		    dir != NULL || mflag || comment != NULL) {
269 			if (is_role(usertype))
270 				errmsg(M_ARUSAGE);
271 			else
272 				errmsg(M_AUSAGE);
273 			exit(EX_SYNTAX);
274 		}
275 
276 		/* Group must be an existing group */
277 		if (group != NULL) {
278 			switch (valid_group(group, &g_ptr, &warning)) {
279 			case INVALID:
280 				errmsg(M_INVALID, group, "group id");
281 				exit(EX_BADARG);
282 				/*NOTREACHED*/
283 			case TOOBIG:
284 				errmsg(M_TOOBIG, "gid", group);
285 				exit(EX_BADARG);
286 				/*NOTREACHED*/
287 			case RESERVED:
288 			case UNIQUE:
289 				errmsg(M_GRP_NOTUSED, group);
290 				exit(EX_NAME_NOT_EXIST);
291 			}
292 			if (warning)
293 				warningmsg(warning, group);
294 
295 			usrdefs->defgroup = g_ptr->gr_gid;
296 			usrdefs->defgname = g_ptr->gr_name;
297 
298 		}
299 
300 		/* project must be an existing project */
301 		if (projects != NULL) {
302 			switch (valid_project(projects, &p_ptr, mybuf,
303 			    sizeof (mybuf), &warning)) {
304 			case INVALID:
305 				errmsg(M_INVALID, projects, "project id");
306 				exit(EX_BADARG);
307 				/*NOTREACHED*/
308 			case TOOBIG:
309 				errmsg(M_TOOBIG, "projid", projects);
310 				exit(EX_BADARG);
311 				/*NOTREACHED*/
312 			case UNIQUE:
313 				errmsg(M_PROJ_NOTUSED, projects);
314 				exit(EX_NAME_NOT_EXIST);
315 			}
316 			if (warning)
317 				warningmsg(warning, projects);
318 
319 			usrdefs->defproj = p_ptr.pj_projid;
320 			usrdefs->defprojname = p_ptr.pj_name;
321 		}
322 
323 		/* base_dir must be an existing directory */
324 		if (base_dir != NULL) {
325 			valid_input(BASEDIR, base_dir);
326 			usrdefs->defparent = base_dir;
327 		}
328 
329 		/* inactivity period is an integer */
330 		if (inactstr != NULL) {
331 			/* convert inactstr to integer */
332 			inact = strtol(inactstr, &ptr, 10);
333 			if (*ptr || inact < 0) {
334 				errmsg(M_INVALID, inactstr,
335 				    "inactivity period");
336 				exit(EX_BADARG);
337 			}
338 
339 			usrdefs->definact = inact;
340 		}
341 
342 		/* expiration string is a date, newer than today */
343 		if (expirestr != NULL) {
344 			if (*expirestr) {
345 				if (valid_expire(expirestr, (time_t *)0)
346 				    == INVALID) {
347 					errmsg(M_INVALID, expirestr,
348 					    "expiration date");
349 					exit(EX_BADARG);
350 				}
351 				usrdefs->defexpire = expirestr;
352 			} else
353 				/* Unset the expiration date */
354 				usrdefs->defexpire = "";
355 		}
356 
357 		if (shell != NULL) {
358 			valid_input(SHELL, shell);
359 			usrdefs->defshell = shell;
360 		}
361 		if (skel_dir != NULL) {
362 			valid_input(SKELDIR, skel_dir);
363 			usrdefs->defskel = skel_dir;
364 		}
365 		update_def(usrdefs);
366 
367 		/* change defaults for useradd */
368 		if (putusrdef(usrdefs, usertype) < 0) {
369 			errmsg(M_UPDATE, "created");
370 			exit(EX_UPDATE);
371 		}
372 
373 		/* Now, display */
374 		dispusrdef(stdout, (D_ALL & ~D_RID), usertype);
375 		exit(EX_SUCCESS);
376 
377 	}
378 
379 	/* ADD mode */
380 
381 	/* check syntax */
382 	if (optind != argc - 1 || (skel_dir != NULL && !mflag)) {
383 		if (is_role(usertype))
384 			errmsg(M_ARUSAGE);
385 		else
386 			errmsg(M_AUSAGE);
387 		exit(EX_SYNTAX);
388 	}
389 
390 	logname = argv[optind];
391 	switch (valid_login(logname, (struct passwd **)NULL, &warning)) {
392 	case INVALID:
393 		errmsg(M_INVALID, logname, "login name");
394 		exit(EX_BADARG);
395 		/*NOTREACHED*/
396 
397 	case NOTUNIQUE:
398 		errmsg(M_USED, logname);
399 		exit(EX_NAME_EXISTS);
400 		/*NOTREACHED*/
401 
402 	case LONGNAME:
403 		errmsg(M_TOO_LONG, logname);
404 		exit(EX_BADARG);
405 		/*NOTREACHED*/
406 	}
407 
408 	if (warning)
409 		warningmsg(warning, logname);
410 	if (uidstr != NULL) {
411 		/* convert uidstr to integer */
412 		errno = 0;
413 		uid = (uid_t)strtol(uidstr, &ptr, (int)10);
414 		if (*ptr || errno == ERANGE) {
415 			errmsg(M_INVALID, uidstr, "user id");
416 			exit(EX_BADARG);
417 		}
418 
419 		switch (valid_uid(uid, NULL)) {
420 		case NOTUNIQUE:
421 			if (!oflag) {
422 				/* override not specified */
423 				errmsg(M_UID_USED, uid);
424 				exit(EX_ID_EXISTS);
425 			}
426 			break;
427 		case RESERVED:
428 			errmsg(M_RESERVED, uid);
429 			break;
430 		case TOOBIG:
431 			errmsg(M_TOOBIG, "uid", uid);
432 			exit(EX_BADARG);
433 			break;
434 		}
435 
436 	} else {
437 
438 		if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
439 			errmsg(M_INVALID, "default id", "user id");
440 			exit(EX_ID_EXISTS);
441 		}
442 	}
443 
444 	if (group != NULL) {
445 		switch (valid_group(group, &g_ptr, &warning)) {
446 		case INVALID:
447 			errmsg(M_INVALID, group, "group id");
448 			exit(EX_BADARG);
449 			/*NOTREACHED*/
450 		case TOOBIG:
451 			errmsg(M_TOOBIG, "gid", group);
452 			exit(EX_BADARG);
453 			/*NOTREACHED*/
454 		case RESERVED:
455 		case UNIQUE:
456 			errmsg(M_GRP_NOTUSED, group);
457 			exit(EX_NAME_NOT_EXIST);
458 			/*NOTREACHED*/
459 		}
460 
461 		if (warning)
462 			warningmsg(warning, group);
463 		gid = g_ptr->gr_gid;
464 
465 	} else gid = usrdefs->defgroup;
466 
467 	if (grps != NULL) {
468 		if (!*grps)
469 			/* ignore -G "" */
470 			grps = (char *)0;
471 		else if (!(gidlist = valid_lgroup(grps, gid)))
472 			exit(EX_BADARG);
473 	}
474 
475 	if (projects != NULL) {
476 		if (! *projects)
477 			projects = (char *)0;
478 		else if (! (projlist = valid_lproject(projects)))
479 			exit(EX_BADARG);
480 	}
481 
482 	/* if base_dir is provided, check its validity; otherwise default */
483 	if (base_dir != NULL)
484 		valid_input(BASEDIR, base_dir);
485 	else
486 		base_dir = usrdefs->defparent;
487 
488 	if (dir == NULL) {
489 		/* set homedir to home directory made from base_dir */
490 		(void) sprintf(homedir, "%s/%s", base_dir, logname);
491 
492 	} else if (REL_PATH(dir)) {
493 		errmsg(M_RELPATH, dir);
494 		exit(EX_BADARG);
495 
496 	} else
497 		(void) strcpy(homedir, dir);
498 
499 	if (mflag) {
500 		/* Does home dir. already exist? */
501 		if (stat(homedir, &statbuf) == 0) {
502 			/* directory exists - don't try to create */
503 			mflag = 0;
504 
505 			if (check_perm(statbuf, uid, gid, S_IXOTH) != 0)
506 				errmsg(M_NO_PERM, logname, homedir);
507 		}
508 	}
509 	/*
510 	 * if shell, skel_dir are provided, check their validity.
511 	 * Otherwise default.
512 	 */
513 	if (shell != NULL)
514 		valid_input(SHELL, shell);
515 	else
516 		shell = usrdefs->defshell;
517 
518 	if (skel_dir != NULL)
519 		valid_input(SKELDIR, skel_dir);
520 	else
521 		skel_dir = usrdefs->defskel;
522 
523 	if (inactstr != NULL) {
524 		/* convert inactstr to integer */
525 		inact = strtol(inactstr, &ptr, 10);
526 		if (*ptr || inact < 0) {
527 			errmsg(M_INVALID, inactstr, "inactivity period");
528 			exit(EX_BADARG);
529 		}
530 	} else inact = usrdefs->definact;
531 
532 	/* expiration string is a date, newer than today */
533 	if (expirestr != NULL) {
534 		if (*expirestr) {
535 			if (valid_expire(expirestr, (time_t *)0) == INVALID) {
536 				errmsg(M_INVALID, expirestr, "expiration date");
537 				exit(EX_BADARG);
538 			}
539 			usrdefs->defexpire = expirestr;
540 		} else
541 			/* Unset the expiration date */
542 			expirestr = (char *)0;
543 
544 	} else expirestr = usrdefs->defexpire;
545 
546 	import_def(usrdefs);
547 
548 	/* must now call passmgmt */
549 
550 	/* set up arguments to  passmgmt in nargv array */
551 	nargv = malloc((30 + nkeys * 2) * sizeof (char *));
552 	argindex = 0;
553 	nargv[argindex++] = PASSMGMT;
554 	nargv[argindex++] = "-a";	/* add */
555 
556 	if (comment != NULL) {
557 		/* comment */
558 		nargv[argindex++] = "-c";
559 		nargv[argindex++] = comment;
560 	}
561 
562 	/* flags for home directory */
563 	nargv[argindex++] = "-h";
564 	nargv[argindex++] = homedir;
565 
566 	/* set gid flag */
567 	nargv[argindex++] = "-g";
568 	(void) sprintf(gidstring, "%u", gid);
569 	nargv[argindex++] = gidstring;
570 
571 	/* shell */
572 	nargv[argindex++] = "-s";
573 	nargv[argindex++] = shell;
574 
575 	/* set inactive */
576 	nargv[argindex++] = "-f";
577 	(void) sprintf(inactstring, "%ld", inact);
578 	nargv[argindex++] = inactstring;
579 
580 	/* set expiration date */
581 	if (expirestr != NULL) {
582 		nargv[argindex++] = "-e";
583 		nargv[argindex++] = expirestr;
584 	}
585 
586 	/* set uid flag */
587 	nargv[argindex++] = "-u";
588 	(void) sprintf(uidstring, "%u", uid);
589 	nargv[argindex++] = uidstring;
590 
591 	if (oflag) nargv[argindex++] = "-o";
592 
593 	if (nkeys > 1)
594 		addkey_args(nargv, &argindex);
595 
596 	/* finally - login name */
597 	nargv[argindex++] = logname;
598 
599 	/* set the last to null */
600 	nargv[argindex++] = NULL;
601 
602 	/* now call passmgmt */
603 	ret = PEX_FAILED;
604 	/*
605 	 * If call_passmgmt fails for any reason other than PEX_BADUID, exit
606 	 * is invoked with an appropriate error message. If PEX_BADUID is
607 	 * returned, then if the user specified the ID, exit is invoked
608 	 * with an appropriate error message. Otherwise we try to pick a
609 	 * different ID and try again. If we run out of IDs, i.e. no more
610 	 * users can be created, then -1 is returned and we terminate via exit.
611 	 * If PEX_BUSY is returned we increment a count, since we will stop
612 	 * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately
613 	 * terminate the loop.
614 	 */
615 	while (busy < 3 && ret != PEX_SUCCESS) {
616 		switch (ret = call_passmgmt(nargv)) {
617 		case PEX_SUCCESS:
618 			break;
619 		case PEX_BUSY:
620 			busy++;
621 			break;
622 		case PEX_HOSED_FILES:
623 			errmsg(M_HOSED_FILES);
624 			exit(EX_INCONSISTENT);
625 			break;
626 
627 		case PEX_SYNTAX:
628 		case PEX_BADARG:
629 			/* should NEVER occur that passmgmt usage is wrong */
630 			if (is_role(usertype))
631 				errmsg(M_ARUSAGE);
632 			else
633 				errmsg(M_AUSAGE);
634 			exit(EX_SYNTAX);
635 			break;
636 
637 		case PEX_BADUID:
638 			/*
639 			 * The uid has been taken. If it was specified by a
640 			 * user, then we must fail. Otherwise, keep trying
641 			 * to get a good uid until we run out of IDs.
642 			 */
643 			if (uidstr != NULL) {
644 				errmsg(M_UID_USED, uid);
645 				exit(EX_ID_EXISTS);
646 			} else {
647 				if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
648 					errmsg(M_INVALID, "default id",
649 					    "user id");
650 					exit(EX_ID_EXISTS);
651 				}
652 				(void) sprintf(uidstring, "%u", uid);
653 			}
654 			break;
655 
656 		case PEX_BADNAME:
657 			/* invalid loname */
658 			errmsg(M_USED, logname);
659 			exit(EX_NAME_EXISTS);
660 			break;
661 
662 		default:
663 			errmsg(M_UPDATE, "created");
664 			exit(ret);
665 			break;
666 		}
667 	}
668 	if (busy == 3) {
669 		errmsg(M_UPDATE, "created");
670 		exit(ret);
671 	}
672 
673 	/* add group entry */
674 	if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) {
675 		errmsg(M_UPDATE, "created");
676 		cleanup(logname);
677 		exit(EX_UPDATE);
678 	}
679 
680 	/* update project database */
681 	if ((projects != NULL) &&
682 	    edit_project(logname, (char *)NULL, projlist, 0)) {
683 		errmsg(M_UPDATE, "created");
684 		cleanup(logname);
685 		exit(EX_UPDATE);
686 	}
687 
688 	/* create home directory */
689 	if (mflag &&
690 	    (create_home(homedir, skel_dir, uid, gid) != EX_SUCCESS)) {
691 		(void) edit_group(logname, (char *)0, (int **)0, 1);
692 		cleanup(logname);
693 		exit(EX_HOMEDIR);
694 	}
695 
696 	return (ret);
697 }
698 
699 static void
700 cleanup(logname)
701 char *logname;
702 {
703 	char *nargv[4];
704 
705 	nargv[0] = PASSMGMT;
706 	nargv[1] = "-d";
707 	nargv[2] = logname;
708 	nargv[3] = NULL;
709 
710 	switch (call_passmgmt(nargv)) {
711 	case PEX_SUCCESS:
712 		break;
713 
714 	case PEX_SYNTAX:
715 		/* should NEVER occur that passmgmt usage is wrong */
716 		if (is_role(usertype))
717 			errmsg(M_ARUSAGE);
718 		else
719 			errmsg(M_AUSAGE);
720 		break;
721 
722 	case PEX_BADUID:
723 		/* uid is used - shouldn't happen but print message anyway */
724 		errmsg(M_UID_USED, uid);
725 		break;
726 
727 	case PEX_BADNAME:
728 		/* invalid loname */
729 		errmsg(M_USED, logname);
730 		break;
731 
732 	default:
733 		errmsg(M_UPDATE, "created");
734 		break;
735 	}
736 }
737 
738 /* Check the validity for shell, base_dir and skel_dir */
739 
740 void
741 valid_input(path_opt_t opt, const char *input)
742 {
743 	struct stat	statbuf;
744 
745 	if (REL_PATH(input)) {
746 		errmsg(M_RELPATH, input);
747 		exit(EX_BADARG);
748 	}
749 	if (stat(input, &statbuf) == -1) {
750 		errmsg(M_INVALID, input, "path");
751 		exit(EX_BADARG);
752 	}
753 	if (opt == SHELL) {
754 		if (!S_ISREG(statbuf.st_mode) ||
755 		    (statbuf.st_mode & 0555) != 0555) {
756 			errmsg(M_INVALID, input, "shell");
757 			exit(EX_BADARG);
758 		}
759 	} else {
760 		if (!S_ISDIR(statbuf.st_mode)) {
761 			errmsg(M_INVALID, input, "directory");
762 			exit(EX_BADARG);
763 		}
764 	}
765 }
766